Skip to content

Add JSON editor for custom URL inline bot rules#62

Merged
omg-xtao merged 12 commits into
NextAlone:dev3from
BraveSail:pr/custom-inline-bot-rules-json
May 5, 2026
Merged

Add JSON editor for custom URL inline bot rules#62
omg-xtao merged 12 commits into
NextAlone:dev3from
BraveSail:pr/custom-inline-bot-rules-json

Conversation

@BraveSail
Copy link
Copy Markdown
Contributor

@BraveSail BraveSail commented Apr 28, 2026

Summary

This PR makes URL auto inline bot rules local and user-configurable from Experimental Settings.

Users can manage multiple URL matching rules in a row-based editor. Each rule maps a matched URL/host to the inline bot username that should be used.

The editor now has two explicit modes:

  • Simple mode: enter a host only, for example x.com. The saved backend rule still uses a regex, but the UI only accepts valid host input. A simple host rule matches the host itself and all deeper subdomains, for example x.com, a.x.com, and b.a.x.com.
  • Advanced mode: enter the regex pattern directly.

Rules are evaluated in the displayed list order. If both x.com and a.x.com exist, whichever row matches first wins.

Functional changes

  • Adds FixUrlAutoInlineBotRules as a string config value storing a JSON array of rules.
  • Adds FixUrlAutoInlineBotRulesAdvancedMode so the selected Simple/Advanced editor mode is persisted instead of being inferred only from saved patterns.
  • Adds a row-based rules editor in NekoExperimentalSettingsActivity with editable pattern/host and bot username fields.
  • In Simple mode:
    • accepts host input only
    • rejects URL/regex-style/non-host input before saving
    • serializes hosts into backend regex patterns that match the host itself and all subdomains
    • displays host values by safely converting known host-backed regex patterns back to host text
  • In Advanced mode:
    • allows raw regex patterns
    • validates every non-empty regex with Pattern.compile(..., CASE_INSENSITIVE) before saving
    • keeps the dialog open, focuses the invalid row, and shows an error if validation fails
  • Blocks Advanced → Simple conversion when a custom regex cannot be safely represented as a host, avoiding silent data loss or corrupted rules.
  • Normalizes bot usernames by stripping a leading @ before saving/parsing.
  • Removes the previous remote-rule refresh path and uses the local config as the single source of truth.
  • Adds built-in default rules for:
    • x.comtwpicbot
    • twitter.comtwpicbot
    • threads.comlizardman_bot
    • threads.netlizardman_bot
    • pixiv.netpixiv_bot
  • Tightens default host matching so patterns require real host boundaries and escaped dots, avoiding broad matches such as twitter-com, notwitter.com, or query-only matches inside unrelated URLs.
  • Adds FixUrlAutoInlineBotSkipMediaPreview as an opt-in switch for direct media inline result sending. It is off by default, so normal photo/gif/video inline bot results still open PhotoViewer for preview/selection. When enabled, direct sending only applies to automatic URL inline bot searches.
  • Polishes the editor dialog layout with bounded scrolling, fixed row heights, centered controls, and complete i18n strings.
  • Merges/syncs the latest dev branch state used by this PR branch.

Review follow-up covered

  • Restored the safe default media inline result behavior and gated instant media sending behind an opt-in setting that only applies to automatic URL inline bot searches.
  • Added save-time regex validation so invalid Advanced-mode patterns cannot appear to save successfully while being ignored at runtime.
  • Replaced broad default domain regexes with host-boundary-safe patterns.
  • Clarified Simple mode semantics: host-only input, host plus infinite subdomains, list-order priority.

Config format

Rules are saved as JSON. Backend matching remains regex-based for compatibility:

[
  {"pattern":"(^|[\\s(\\[{<])(?:https?://)?([\\w-]+\\.)*x\\.com(?=[:/?#\\s)\\]}>]|$)","bot":"twpicbot"},
  {"pattern":"(^|[\\s(\\[{<])(?:https?://)?([\\w-]+\\.)*twitter\\.com(?=[:/?#\\s)\\]}>]|$)","bot":"twpicbot"}
]

Empty rows are skipped when saving. Existing invalid patterns that somehow reach runtime are logged and skipped.

Notes

  • The old temporary rule => bot_username free-form format is intentionally not kept for long-term compatibility, because it was only introduced on this feature branch before the JSON editor change.
  • Existing custom regex rules force Advanced mode on first open for compatibility.
  • The media inline result behavior is deliberately conservative by default: existing inline bot preview behavior is preserved unless the new direct-send switch is explicitly enabled.
  • Release keystore and native integrity changes are intentionally excluded from this PR branch.

Testing

  • git diff --check passes.
  • Parsed updated XML string resources successfully.
  • Parsed the default rules JSON successfully.
  • Compiled all default regex patterns with Java Pattern successfully.
  • Ran lightweight Java assertion checks for Simple-mode host matching:
    • x.com matches x.com, a.x.com, and b.a.x.com
    • a.x.com does not match plain x.com
    • unrelated hosts do not match
    • list order determines which bot wins when multiple rules match
  • GitHub Actions Release Build Merge Upstream Update #42 passed for the source feature branch before the PR sync: https://github.com/BraveSail/Nagram/actions/runs/25282568907

The PR branch now contains only the inline bot rules/settings follow-up changes on top of the prior PR branch state.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 28, 2026

Reviewer's Guide

Implements a local, JSON-based configuration and UI editor for URL inline bot rules, replaces the previous remote-fetch and serialized storage mechanism, and simplifies inline bot result sending behavior for media results.

Sequence diagram for applying URL inline bot rules when checking a URL

sequenceDiagram
    actor User
    participant ChatActivity
    participant InlineBotRulesHelper
    participant NaConfig

    User->>ChatActivity: Type_or_paste_message_text
    ChatActivity->>InlineBotRulesHelper: doRegex(textToCheck)
    InlineBotRulesHelper->>NaConfig: getFixUrlAutoInlineBot().Bool()
    NaConfig-->>InlineBotRulesHelper: fixUrlAutoInlineBot_enabled
    InlineBotRulesHelper->>NaConfig: getFixUrlAutoInlineBotRules().String()
    NaConfig-->>InlineBotRulesHelper: jsonRulesString
    InlineBotRulesHelper->>InlineBotRulesHelper: loadInlineBotRules()
    InlineBotRulesHelper->>InlineBotRulesHelper: parseInlineBotRules(jsonRulesString,true)
    InlineBotRulesHelper->>InlineBotRulesHelper: Compile_case_insensitive_regex_patterns
    InlineBotRulesHelper->>InlineBotRulesHelper: Iterate_rules_and_match_text
    InlineBotRulesHelper-->>ChatActivity: matchingBotUsername_or_null
    ChatActivity-->>User: Use_inline_bot_username_if_not_null
Loading

Updated class diagram for InlineBotRulesHelper and related configuration and UI

classDiagram

    class InlineBotRulesHelper {
        -static InlineBotRulesHelper Instance
        -ArrayList~InlineBotRule~ inlineBotRules
        -String loadedRules
        +static InlineBotRulesHelper getInstance()
        -void loadInlineBotRules()
        +String doRegex(String textToCheck)
        +static ArrayList~InlineBotRule~ parseInlineBotRules(String list, boolean compilePattern)
        -static void addInlineBotRule(ArrayList~InlineBotRule~ rules, String username, String rule, boolean compilePattern)
        +static String serializeInlineBotRules(ArrayList~InlineBotRule~ rules)
    }

    class InlineBotRule {
        +String username
        +String rule
        +Pattern regexPattern
        +InlineBotRule()
        +InlineBotRule(String username, String rule)
        +InlineBotRule(String username, String rule, boolean compilePattern)
        +void buildRegexPattern()
    }

    class NaConfig {
        <<object>>
        +ConfigItem fixUrlAutoInlineBot
        +ConfigItem fixUrlAutoInlineBotRules
        +NaConfig INSTANCE
    }

    class ConfigItem {
        +String key
        +int type
        +String String()
        +boolean Bool()
        +void setConfigString(String value)
        +String getKey()
    }

    class NekoExperimentalSettingsActivity {
        -AbstractConfigCell fixUrlAutoInlineBotRow
        -AbstractConfigCell fixUrlAutoInlineBotRulesRow
        +void showFixUrlAutoInlineBotRulesDialog()
        -void addFixUrlAutoInlineBotRuleRow(Context context, LinearLayout rowsContainer, ArrayList~EditTextBoldCursor[]~ ruleRows, String rule, String username)
    }

    class ConfigCellTextCheck {
    }

    class ConfigCellTextInput {
    }

    class NaConfig_usage_in_UI {
    }

    InlineBotRulesHelper "1" *-- "*" InlineBotRule : contains
    NaConfig "1" o-- "1" ConfigItem : defines
    NekoExperimentalSettingsActivity ..> InlineBotRulesHelper : uses_parse_and_serialize_rules
    NekoExperimentalSettingsActivity ..> NaConfig : reads_and_writes_FixUrlAutoInlineBotRules
    NekoExperimentalSettingsActivity ..> ConfigCellTextCheck : uses
    NekoExperimentalSettingsActivity ..> ConfigCellTextInput : uses
    NaConfig_usage_in_UI ..> NaConfig : singleton_ACCESS_via_INSTANCE
Loading

File-Level Changes

Change Details Files
Replace remote inline bot rules helper with local JSON-based storage and regex matching.
  • Remove BaseRemoteHelper inheritance, remote loading, serialization, and update logic, keeping a singleton helper focused on in-memory rules
  • Load rules from NaConfig FixUrlAutoInlineBotRules string, caching last-loaded value to avoid redundant parsing
  • Introduce JSON parsing of rules with backwards-compatible field names and optional regex compilation
  • Normalize bot usernames by stripping leading '@' when adding rules and compiling a single case-insensitive Pattern per rule
  • Add serialization helper to export rules back to JSON for storage
TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/remote/InlineBotRulesHelper.java
Add experimental settings UI for editing URL inline bot rules as row-based JSON-backed form inputs.
  • Register a new ConfigItem fixUrlAutoInlineBotRules in NaConfig as a string setting
  • Add a new text-input config row in NekoExperimentalSettingsActivity that opens a custom dialog
  • Implement a dialog with a scrollable list of rule rows (pattern + username EditText) and a '+' button to append rows
  • Pre-populate rows from existing JSON rules (without compiling patterns) and normalize usernames when saving
  • On save, serialize collected rules to JSON, store in NaConfig, refresh the affected settings row and trigger callbacks
TMessagesProj/src/main/kotlin/xyz/nextalone/nagram/NaConfig.kt
TMessagesProj/src/main/java/tw/nekomimi/nekogram/settings/NekoExperimentalSettingsActivity.java
Adjust inline bot result selection behavior to always go through the paid-message confirmation path instead of opening a media preview.
  • In ChatActivity, remove special-case handling that opened PhotoViewer for photo/gif/video inline results, always using AlertsCreator.ensurePaidMessageConfirmation instead
  • In MentionsContainerView, remove PhotoViewer-based preview for media inline results and always delegate sendBotInlineResult directly
TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java
Wire up new inline bot rules config key and strings, and stop triggering remote rules refresh at startup.
  • Add localized string resources for the rules editor title, field hints, and labels in English and Simplified Chinese
  • Remove LaunchActivity startup call that previously triggered InlineBotRulesHelper.checkInlineBotRules, since rules are now purely local
TMessagesProj/src/main/res/values/strings_na.xml
TMessagesProj/src/main/res/values-zh-rCN/strings_na.xml
TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • In showFixUrlAutoInlineBotRulesDialog, you manually strip a leading @ from usernames while InlineBotRulesHelper.addInlineBotRule also normalizes usernames; consider centralizing this normalization in a single helper to avoid subtle divergence in behavior over time.
  • The + button is created for every rule row in addFixUrlAutoInlineBotRuleRow, which allows adding new rows from any position; if that’s not intentional, you could restrict the add button to the last row to keep the layout simpler and avoid accidental insertions in the middle.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `showFixUrlAutoInlineBotRulesDialog`, you manually strip a leading `@` from usernames while `InlineBotRulesHelper.addInlineBotRule` also normalizes usernames; consider centralizing this normalization in a single helper to avoid subtle divergence in behavior over time.
- The `+` button is created for every rule row in `addFixUrlAutoInlineBotRuleRow`, which allows adding new rows from any position; if that’s not intentional, you could restrict the add button to the last row to keep the layout simpler and avoid accidental insertions in the middle.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@BraveSail BraveSail force-pushed the pr/custom-inline-bot-rules-json branch from b0517b2 to 6cc1189 Compare April 28, 2026 13:40
@BraveSail
Copy link
Copy Markdown
Contributor Author

BraveSail commented Apr 28, 2026

Addressed Sourcery's feedback in the latest push:

  • Centralized inline bot username normalization in InlineBotRulesHelper.normalizeInlineBotUsername(...) and reused it from both parsing/serialization and the settings UI.
  • Changed the row editor so only the last row shows an active + button, avoiding accidental insertions from earlier rows.

BraveSail added a commit to BraveSail/Nagram that referenced this pull request Apr 29, 2026
Port the small follow-up changes from upstream PR NextAlone#62 onto the current branch after merging latest upstream/dev.

Normalize bot usernames in one helper and only show the add button on the last rule row.
BraveSail added 10 commits May 1, 2026 10:59
Replace the cramped two-column inline bot rules editor with stacked card rows inside the experimental settings dialog.

Add a clear hint, a full-width add control, per-row delete buttons, and shared EditText styling for URL regex and inline bot username fields.

Validation: git diff --check passed. Full Gradle configuration remains blocked locally by missing Android NDK 26.1.10909125.
Populate FixUrlAutoInlineBotRules with the five default rules shown in the settings screenshot: x.com and twitter.com use twpicbot, threads.com and threads.net use lizardman_bot, and pixiv.net uses pixiv_bot.

The branch was already merged with the latest upstream/dev before this change. Verified the embedded JSON parses successfully.
Restore the original PhotoViewer media preview path for regular photo/gif/video inline bot results so manual inline bot usage no longer sends media immediately.

Add FixUrlAutoInlineBotSkipMediaPreview as an opt-in setting. When enabled, the instant send behavior only applies to automatic URL inline bot searches detected by MentionsAdapter; other inline bot results keep the preview/selection flow.

Validation: git diff --check; XML parsing for updated string resources; JSON parsing for the default URL inline bot rules. Full Gradle validation is blocked in this container because Android SDK/local.properties is not configured.
Reject invalid regex patterns in the custom URL inline bot rules dialog before saving. The dialog now keeps focus on the invalid row and shows which rule failed, instead of accepting a value that later gets ignored at runtime.

Tighten the built-in default rules so dots are escaped and matches are bounded to URL-like hosts. This avoids broad matches such as twitter-com, notwitter.com, and query-only mentions on unrelated URLs.

Also log runtime parse failures in InlineBotRulesHelper instead of silently swallowing them. Validation: git diff --check, XML parsing for updated string resources, JSON parsing for default rules, and Java Pattern compilation for all default regexes.
The PR workflow's Debug Build step was assembling the release variant after removing the release signing config, producing unsigned release APK artifacts. Those artifacts are not suitable for direct smoke testing and can behave differently from the intended debug package.

Switch the PR workflow to assembleDebug and replace the release signing config with the default debug signing config, so uploaded PR artifacts are debuggable debug APKs intended for installation/testing.

Validation: git diff --check.
Sync only the inline bot rule editor follow-up changes into the PR branch.

Keep simple mode host-only, serialize simple hosts as backend regex rules, persist advanced mode, validate regex/host inputs, and block unsafe advanced-to-simple conversion.

Also includes the editor layout/string follow-ups needed by these settings UI changes. Excludes release keystore and native integrity changes from the PR branch.

Validation: git diff --check.
@BraveSail BraveSail force-pushed the pr/custom-inline-bot-rules-json branch from 72df582 to 3deec65 Compare May 3, 2026 15:38
@omg-xtao omg-xtao changed the base branch from dev to dev3 May 5, 2026 12:56
@omg-xtao omg-xtao added the enhancement New feature or request label May 5, 2026
@omg-xtao omg-xtao merged commit c2359fb into NextAlone:dev3 May 5, 2026
5 checks passed
omg-xtao added a commit that referenced this pull request May 5, 2026
- 新增 InlineBotRulesRepository 作为统一规则仓库,管理远程(只读)和本地(可编辑)规则
- 新增 InlineBotRulesSettingActivity 规则设置页面,支持远程规则开关和本地规则 CRUD
- 新增 InlineBotRulesEditActivity 规则编辑页面
- 在 NekoExperimentalSettingsActivity 中整合相关设置项,形成独立的功能模块

Co-authored-by: BraveSail <49320262+BraveSail@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants