Tags: dam2452/RanchBot
Tags
Hot patch: Switch Signal client to HTTP polling (#152) * Use WebSocket for Signal receive loop Replace HTTP polling with a persistent WebSocket receive loop. Adds _WS_RECONNECT_DELAY constant and json import, connects to /v1/receive/<phone> via ws_connect, processes TEXT frames with json.loads and schedules handler tasks, and handles WS ERROR/CLOSED by reconnecting after a delay. Preserves pending task tracking and re-raises asyncio.CancelledError. Updates logging to reflect WebSocket lifecycle and removes the old poll timeout usage. * Switch Signal client to HTTP polling Bump version to 4.6.1 and replace the WebSocket-based receive loop with HTTP polling. Adds _POLL_INTERVAL and _POLL_TIMEOUT, uses aiohttp.ClientTimeout and resp.raise_for_status(), and parses JSON responses (handling a list of events and scheduling handler tasks). Removes unused json import, updates logging, and special-cases asyncio.CancelledError to re-raise while retrying on other errors.
[4.6.0] Add Signal platform support (#146) * Add Signal platform support Introduce Signal integration using signal-cli JSON-RPC: add Signal adapter (SignalMessage, SignalResponder, SignalRPC) and a signal_runner to process incoming commands and wire handlers/middlewares. Persist Signal users: add signal_user_id_seq and signal_users table/index in init_db.sql and implement DatabaseManager.get_or_create_signal_user. Add settings flags (ENABLE_SIGNAL, SIGNAL_PHONE_NUMBER, SIGNAL_CLI_PATH) and validation, and register the Signal platform in main.py. Also update PermissionLevelFactory to build middlewares by inspecting handler command lists. SignalRPC manages a subprocess with JSON-RPC, SignalResponder strips markdown and handles file cleanup; JSON responses are not supported by the responder. * Signal: encapsulate internals, improve RPC Bump VERSION to 4.2.0 and refactor Signal adapter internals. - Converted many single-underscore attributes to double-underscore (name-mangled) for stronger encapsulation across SignalMessage, SignalResponder and SignalRPC. - Moved markdown stripping into SignalResponder as a private static method and made its regexes private. - Reworked SignalRPC lifecycle and I/O handling: track event handler tasks, validate started state in __call, use asyncio.get_running_loop() for futures, add timeout cleanup, create/track event tasks for incoming events, and set exceptions on pending futures if the connection closes. Stop now cancels and awaits event tasks before terminating the subprocess. - Minor change in signal_runner to ensure rpc.stop() is always awaited in finally (removed a no-op CancelledError catch). Overall these changes strengthen encapsulation and make the Signal RPC code more robust and resilient to process/IO shutdowns. * Switch Signal integration to HTTP API Replace signal-cli RPC integration with an HTTP/WebSocket client (SignalHttpClient). Update SignalResponder and signal runner to use the new client, start/stop the client, and receive events over WS. Remove legacy signal_rpc implementation. Add SIGNAL_API_URL to settings (and validation) and expose new env vars in docker-compose and GitHub Actions. Add docker-compose.signal.yml to run signal-cli-rest-api as a service. * Replace Signal WS with HTTP long-polling Switch the receive loop from a websocket to HTTP long-polling GET requests to /v1/receive/{phone}. Add a client timeout (_POLL_TIMEOUT), call resp.raise_for_status(), and parse the response as a list of messages, creating tasks for each message. Update logging to reflect polling behavior, re-raise asyncio.CancelledError, and retry on other exceptions after a delay. Also adjust imports (remove json, add List) to match the new implementation. * Parse JSON regardless of Content-Type in Signal client Disable aiohttp's Content-Type check when parsing the poll response by calling resp.json(content_type=None). This allows the client to accept JSON bodies even when the server sets a non-JSON Content-Type (e.g. text/plain), preventing parsing errors in the Signal polling handler. * Prefer sourceNumber when parsing Signal events Handle Signal envelope variants by checking `sourceNumber` first and falling back to `source`. This ensures incoming commands are recognized when the envelope uses `sourceNumber` instead of `source`. * Signal runner: validate incoming events Add early exits in _handle_incoming_event to ignore events with data.get("exception") and to skip processing when envelope.get("dataMessage") is missing. Update source extraction to prefer sourceNumber or sourceUuid instead of falling back to empty strings or a different key. These checks prevent errors when payloads are incomplete or malformed. * Add debug logs for Signal events Add debug logging in signal_runner to record the raw incoming event, log and skip events that contain an exception, show envelope keys when dataMessage is missing, and log the resolved source and text. These logs help diagnose message parsing and early-return conditions during Signal event handling. * Elevate Signal handler logs to WARNING Change several logging calls in the Signal incoming-event handler from DEBUG to WARNING (raw event, missing dataMessage, and source/text). Also remove the debug message that was emitted when an event contained an exception. This surfaces Signal input issues more prominently. * Signal: attachments, reactions, and parsing Extend Signal adapter with richer messaging features and more robust parsing. Key changes: - SignalHttpClient: increased poll timeout, added mimetypes import and _ATTACHMENT_LIMIT_MB; added send_text (styled + quote support), send_attachment, send_file (with size check and mime detection), send_reaction, delete_reaction, send_read_receipt, set_typing, download_attachment, and session helpers (__ensure_session, __put, __delete). Use session.ensure and raise_for_status consistently. - SignalMessage: include optional display_name and prefer it for get_full_name. - SignalResponder: improved markdown handling (unescape for MarkdownV2), split text sending helpers, send_photo now uses send_attachment with proper mime; video send enforces size limit and raises VideoTooLargeException; captions are unescaped before sending. - signal_runner: improved logging formatting, ignore empty messages, parse sourceUuid/sourceNumber/sourceName robustly, choose recipient (uuid preferred), persist user by number, adapt to renamed handler factory method, and tidy startup logs. These changes add support for attachments, reactions, receipts, typing indicators, styled text, and safer file handling while improving logging and message parsing. * Use ATTACHMENT_LIMIT_MB constant for uploads Introduce a class-level _ATTACHMENT_LIMIT_MB (95) in SignalResponder and replace the hardcoded 95 comparison with this constant. This centralizes the attachment size limit, making the value easier to find and adjust in the future. * Increase Signal responder max message length Raise the Signal responder's _MAX_MESSAGE_LENGTH constant from 2000 to 10000 characters to allow handling of much longer messages. No other behavior or limits (e.g. attachment limit) were changed. * Signal client improvements and settings validation Refactor Signal adapter and runner behavior and simplify settings validation. - bot/adapters/signal/signal_http_client.py: Track pending handler tasks, prevent multiple receive loops, offload file reads to a thread, centralize response checking with error logging before raising, and create/track handler tasks so they are cleaned up when done. - bot/platforms/signal_runner.py: Adjust logging levels (raw event -> debug, incoming message -> info) and tighten command parsing using split(maxsplit=1). - bot/settings.py: Change default SIGNAL_API_URL to empty and replace the long conditional validator with a concise requirements check that ensures at least one platform is enabled and that enabled platforms have their required settings.
[4.5.0] English language support, Multi-Series, REST Batch API, searc… …h and database optimizations (#151) * Set YUVJ420P pixel format for keyframe extraction Add -vf format=yuvj420p to the ffmpeg args in bot/video/keyframe_extractor.py so extracted keyframe images use a JPEG-compatible pixel format. This prevents color/profile and compatibility issues when writing thumbnails and ensures consistent output across inputs. * Add REST batch API and factory refactor Introduce REST batch support and decouple factories from aiogram. Added BatchRequest/BatchCommandItem models, a batch executor, RestMiddlewareAdapter, REST/Telegram registrar files and tests. Added Telegram inline protocols and TelegramInlineResponder. Refactored PermissionLevelFactory to remove direct aiogram dependencies (cached handler classes, get_command_handler_pairs, get_middlewares) and updated create_all_factories signature. Converted many BotMessageHandler.get_commands implementations to @classmethod and adjusted SubscribedPermissionLevelFactory inline handler signature to accept Bot. * Add English support to handlers and responses Add English ('en') command variants and locale handling across characters, emotions and objects handlers; introduce Language type and pass lang to formatter functions. Response modules updated to render English messages and full-list hints, and object/character/emotion formatters handle empty results and wording in both languages. Tests added for the new /p_en, /pl_en, /szp_en, /e_en, /obj_en, /objl_en and /szo_en commands. Also make create_access_token default user.full_name to empty string when None to avoid null in JWT payload. * Add EN search commands; pass lang to resolver Require arguments for English search commands by adding "szp_en" and "szo_en" to the search command lists in CharactersHandler and ObjectsHandler. Update ObjectsHandler to pass the resolved Language to __resolve_object_class calls, change the method signature to accept an optional lang (default "pl"), and include lang when reporting an object-not-found error. * Treat en commands as listing-only Docs, handlers and tests updated so *_en commands are treated as list-only triggers. COMMANDS.md and COMMANDSen.md were edited to clarify that /p_en and /obj_en are for English listings (search remains language-agnostic). CharactersHandler and ObjectsHandler no longer include *_en search variants in search command lists; argument validation and language selection were changed to set lang='en' only when the *_en command is used without arguments. Removed tests that relied on English-specific search/name behaviors, keeping only the listing checks. * Add rate-limit bypass and raise message limit Short-circuit rate-limit checks when settings.DISABLE_RATE_LIMITING is enabled by returning True early in BotMiddleware.check_command_limits_and_privileges. Also increase MESSAGE_LIMIT from 5 to 30 in settings to allow more messages before rate-limiting triggers. These changes make it possible to disable rate limiting (e.g., for testing or special deployments) and reduce accidental throttling for normal use. * Support multi-series selection and search Add support for selecting multiple series (or all) for user context and searching. Database schema: add user_series_context.active_series JSONB column and GIN index; provide DatabaseManager.get/set_user_active_series_names to persist JSON lists. SerialContextManager updated to expose list APIs and to set/get lists (empty list = all); existing single-series helpers now fall back to the first selected series. ScenesFinder updated to query multiple ES indices and build proper term/terms filters; logging adjusted to include series list. Many handlers and services updated to accept List[str] (search/filter/clip handlers, filter command flow, active-filter loaders), with clips using the first series when a single series is required. UI/responses: serial messages now display multiple/current-all states and a new error message enforces that filters require exactly one series. Overall this enables multi-series selection, "all" searches, and keeps backward compatibility by using the first series where a single name is needed. * Make active_series column creation idempotent Remove active_series from the table definition and add a conditional DO block that ALTERs the user_series_context table to add the active_series JSONB column only if it does not already exist. This preserves the JSONB DEFAULT NULL behavior and keeps the GIN index creation, making the init_db.sql safe to re-run on databases that may already have the column. * Default user active_series to empty JSON array Change active_series to use an empty JSONB array instead of NULL and simplify handling in code. Migration: set column default to '[]', populate active_series from legacy active_series_id where present, and replace any remaining NULLs with '[]'; keep the GIN index. Code: DatabaseManager.get_user_active_series_names now returns List[str] (never None) and reads active_series via fetchval/json.loads; set_user_active_series_names accepts a List[str] and stores json.dumps(names). Removed legacy active_series_id fallback logic. SerialContextManager updated to use the new non-null list semantics (returns directly and writes [] for clearing). These changes simplify null handling and make active series consistently represented as an array. * Add safe DB backfill and ignore missing ES indices Add active_series column without a non-null default to avoid a full table rewrite on ALTER TABLE; backfill NULLs to '[]' and then set the column default to '[]' afterwards, and keep the GIN index. In ScenesFinder, capture the built index name and call Elasticsearch with ignore_unavailable=True to avoid errors when an index is missing (applies to both search paths). Improves deploy safety and robustness when indices are absent. * Ignore missing ES indices and filter series Make Elasticsearch queries resilient to missing/unavailable indices by adding ignore_unavailable=True to multiple es.search calls across text_segments_finder, character_finder, frames_finder, and object_finder. Add ElasticSearchManager.get_series_with_scenes_index to enumerate series that have *_scenes indices. Update SerialContextManager to import ElasticSearchManager and filter filesystem-scanned series to only those that have scene indices (falling back to filesystem results if none indexed). These changes prevent NotFound errors and ensure only indexed series are treated as available for search. * Set single active series when only one selected Collect series IDs from get_or_create_series and, if exactly one series was provided, set that series as the user's active series. Previously the code called get_or_create_series but ignored the returned IDs, so an explicit active series was not set for single-selection cases. This change initializes a series_ids list, appends each created/fetched ID, and calls set_user_active_series(user_id, id) when there's only one entry; active series names are still stored as before. * Delete last_clips when updating active series Add DatabaseManager.delete_last_clips_by_chat_id which deletes rows from last_clips for a given chat_id. Invoke this method in SerialContextManager.set_user_active_series_list when clearing a user's active series and after updating their active series to ensure stale last_clip records are removed. * Run Uvicorn with 4 workers Replace the environment-based reload flag with a fixed workers=4 argument to uvicorn.run. This starts the REST API with 4 worker processes for increased concurrency and removes the previous automatic reload behavior that depended on ENVIRONMENT, so development auto-reload will no longer be enabled by that check. * Make REST API worker count configurable Add a new REST_API_WORKERS setting (default 4) to bot.settings and use it in bot.platforms.rest_runner.py instead of the hardcoded workers value. This allows configuring the uvicorn worker count via settings or environment variables. * Remove auto-clean triggers for old logs Replace creation of clean_old_system_logs and clean_old_user_logs trigger/functions with DROP TRIGGER IF EXISTS and DROP FUNCTION IF EXISTS ... CASCADE statements. This removes legacy auto-clean behavior for system_logs and user_logs and prevents duplicate trigger/function creation while leaving the clean_old_user_command_limits function intact. * Revamp init_db.sql: new schema, migrations Rework and extend the database initialization script: add authentication (user_credentials, refresh_tokens, verification_tokens), introduce series and user_series_context (with migration from single active_series_id to JSONB active_series and backfill), add user_search_filters, normalize indexes/formatting, and ensure video_clips thumbnail column exists. Implement partitioned user_logs with yearly partitions, keep system_logs, add series_id columns to key tables, and add cleanup/maintenance functions and triggers (including creating/refreshing user_series_context rows for existing users). Misc: create rest_user_id_seq, add/adjust indexes, drop deprecated cleanup triggers, and perform various safety/backfill updates to support new features. * Treat None season as absent when formatting Replace "if 'season' in ep" with an explicit None check (ep.get('season') is not None) when building episode strings. This ensures a season value of None is treated as absent (formats as E##) and avoids errors when attempting to format None as an integer. * Add episode filtering to frame/scene searches Add episode-level filtering across video frame and scene search code paths. Introduce an `episodes: Optional[List[Tuple[int, int]]]` parameter to CharacterFinder, VideoFramesFinder and ObjectFinder methods and use `build_episode_restriction_filter` to append the appropriate Elasticsearch clause when episodes are provided. Update imports to include `build_episode_restriction_filter` and add `_episodes_from_filter` in active_filter_scene_segments to extract (season, episode) pairs from the search filter and pass them into finder calls. * Support seasons list for episodes without season Allow episode entries that omit 'season' to be expanded using the 'seasons' list in the search filter. If an entry lacks 'episode' it is skipped. When 'season' is missing but 'seasons' is present, a (season, episode) pair is created for each season; entries with both season and episode keep their original behavior. Returns None if no valid pairs are found. * Add nested character+emotion query clause Update search filter handling to combine character_groups with emotions when both are provided. Introduce _nested_character_emotion_clause to build a nested Elasticsearch query that matches character names and mapped English emotion labels within the same frame/character nested path. Adjusted branching so character_groups are only added by themselves when emotions are absent, and emotions fall back to the existing nested emotion clause when no character_groups are present. This ensures characters are matched together with their emotions (instead of independently) when both filters are supplied. * Enforce minimum face & emotion confidence Introduce minimum confidence thresholds in ScenesFinder (bot/search/scenes_finder.py) to reduce low-confidence detections. Added _MIN_FACE_CONFIDENCE (0.5) and _MIN_EMOTION_CONFIDENCE (0.3) constants and applied RANGE filters (GTE) on actor confidence and actor.emotion.confidence in the character/emotion query paths, including the combined character+emotion filter block. This ensures queries ignore detections below the configured confidence levels, improving result relevance and reducing false positives. * Use duration for keyframe extraction Switch keyframe extraction to use clip.duration (0.0..duration) and guard on duration > 0 instead of comparing start/end times. Also simplify the fallback seek_time to duration * 0.1 when no keyframes are found. This handles clips with missing/invalid start/end timestamps and ensures keyframes and seek positions are computed relative to the clip's actual duration. * Add Polish label to detected objects Populate a new label_pl field for detected objects and update the ObjectWithCount type accordingly. ObjectFinder now calls get_polish_name(...) when building objects so each object includes a Polish label (label_pl) alongside class_name and scene_count. This enables localized display of object names. * Update expected file hashes Regenerate and update checksums in bot/tests/expected_file_hashes.json for several media and message fixtures. Updated entries include clip_filter_sezon1*, multiple search_* message hashes and related localization keys to keep tests in sync with regenerated fixtures. * Update expected file hashes in tests Refresh SHA256 values in bot/tests/expected_file_hashes.json to match updated test fixtures. Updated entries: list_krowa.txt, sd_geniusz.mp4, sd_sadjust_geniusz.mp4, and sd_sdostosuj_geniusz.mp4 with their new hash values. * Fix response handling and update tests Fix a bug in StartHandler by retrieving the fallback response callable (get_invalid_command_message) without prematurely calling it. Replace direct _responder.send_text calls in InlineClipHandler with _reply_warning/_reply_error helpers for consistent reply handling. Update tests and fixtures to match behavior/API changes: adjust expected file hash for search_anglii_results.message, add '/serial Ranczo' setup to many filter/search tests so an active series is selected, change serial-change test expectations to use a list argument, and update batch test to expect 401 instead of 403. These changes align handlers and tests with the corrected response APIs and updated behavior. * Update expected file hashes Regenerate expected SHA256 values in bot/tests/expected_file_hashes.json to match updated test fixtures (likely due to re-encoding or fixture changes). Updated entries include: clip_filter_sezon1, clip_filter_sezon1_geniusz, sd_geniusz, sd_sadjust_geniusz, sd_sdostosuj_geniusz, search_filter_sezon1, and search_filter_sezon1_geniusz. * Update VERSION
[4.4.2] Centralize message splitting in AbstractResponder (#150) * Centralize message splitting in AbstractResponder Move message-splitting logic into AbstractResponder and provide default send_text/send_markdown implementations that split long messages and call abstract per-part methods (_send_text_part/_send_markdown_part). Update concrete responders (Telegram/REST) to implement the per-part methods and set a platform max length (Telegram _MAX_MESSAGE_LENGTH=4096). Remove ad-hoc splitting from EpisodeListHandler. The new splitter preserves fenced code blocks when splitting and exposes _MAX_MESSAGE_LENGTH for platform-specific limits. * Propagate reply_to_id across responders Allow multipart messages to reply to the previous part by threading replies. AbstractResponder now tracks the last sent message id and passes reply_to_id into _send_text_part/_send_markdown_part, which now return Optional[int]. Telegram responder returns the sent message_id and uses it as the next reply target; REST responder accepts the param but still returns None. Also bump version 4.4.1 -> 4.4.2. * Use callable default in response lookup Pass the get_invalid_command_message function itself as the default to __RESPONSES.get so the returned value is a callable before invoking it. This prevents attempting to call a non-callable (e.g. a string) when a command is missing, avoiding a TypeError at runtime.
Set YUVJ420P pixel format for keyframe extraction (#149) Add -vf format=yuvj420p to the ffmpeg args in bot/video/keyframe_extractor.py so extracted keyframe images use a JPEG-compatible pixel format. This prevents color/profile and compatibility issues when writing thumbnails and ensures consistent output across inputs.
[4.4.0] REST API integration, new commands, ES improvements & core fi… …xes (#148) REST and Auth: - Added REST-side user registration and password reset. - Telegram and REST API account linking (added `/link` and `/kodkonta` commands). - `RestResponder` can now return structured JSON (`prefer_json`) with metadata and errors instead of immediately sending a file or plain HTTP response. New user commands: - `/klatka` (`/kl` / `/frame`) – extracts a single keyframe. Uses a two-step ffmpeg seek for better precision. - `/zapisznumer` (`/zn`) – saves a clip based on its index from recent search results (includes limit checks and trimming). - `/klatkaklipu` (`/kk`) – extracts a thumbnail from already saved clips. - Added an optional `[serial]` parameter to `/mojeklipy` (e.g., passing "all" lists clips from all series). Elasticsearch and filters: - Search rebuild: transitioning to `ScenesFinder`. Instead of text-only, we now hit the combined SCENES index (text + frames). - Filters (`FilterApplicator`) are no longer implicitly applied to every search. This behavior was moved to dedicated commands: `/klipfiltr` (`/kf`) and `/szukajfiltr` (`/szf`). - Relevance tweaks (`build_fuzzy_with_boost_query`) – exact phrases get a 3.0 boost, and exact matches get 1.5. - Stability fixes: safe casting of empty scores to 0.0, better pagination, and frame deduplication. Video handling and minor fixes: - Saved clip thumbnails are now stored in the DB (`thumbnail_data` column). - Improved scene snapping algorithm (`SceneSnapService`) – it now takes keyframe offsets into account. - Fix for the `is_adjusted` flag in `AdjustVideoClipHandler` which previously failed to properly detect consecutive adjustments. - Added MarkdownV2 escaping to prevent formatting errors with special characters. - Updated all SHA256 hashes in tests (mainly due to normalizing the Polish 'ł' character in filenames).
[4.0.0] Add semantic search feature + QOL (vLLM + ES) (#143) * Add scene-snap feature and integrate handlers Introduce scene snap functionality and wire it across the bot. Adds SceneSnapService and new SnapClipHandler (plus responses and tests) to align clips to scene cuts; integrates snapping into transcription, clip selection, inline clips, adjust/compile flows and uses active_series where needed. Update Elasticsearch mappings to include segment/scene and embedding index mappings. Miscellaneous: update COMMANDS docs, make TelegramResponder.edit_text static, pass series_name into compilation, small type-hint/return cleanups and formatting fixes, and adjust rest_runner import to avoid protected-access issues. * Add typing and handle optional clip durations Introduce stronger typing across the codebase and add support for optional clip durations. Key changes: - Make VideoClip.duration Optional and propagate None-safety checks in handlers (bot_message_handler, compile_selected_clips_handler, inline_clip_handler) to avoid errors when duration is missing; display "?" for unknown durations. - Expand bot/types.py with concrete TypedDict definitions (SegmentWithScore, ElasticsearchSegment, ClipSegment, TranscriptionContext, and many others) and update imports throughout handlers and services. - Add/clarify type annotations for ES queries and responses, mp4_map (Dict[str, Path]) and SeriesScanner.__extract_episode_code (Optional[str]). - Use pathlib.Path where appropriate (snap_clip_handler, ClipsExtractor call) and minor refactors (return temp variable 'result' in compile_clips_handler). - Replace plain string constants with typed Final[str] constants in bot/utils/constants.py for improved type safety. These changes improve type safety, make edge cases (missing durations) explicit, and prevent runtime errors due to unexpected None or wrong types. * Use scene_start_time/scene_end_time fields Replace references to scene_info.start.seconds and scene_info.end.seconds with scene_info.scene_start_time and scene_info.scene_end_time. Update the Elasticsearch _source includes and the extraction logic so scene start/end times are read from the new flattened field names. Ensure index mappings or data are migrated to provide the new fields. * Apply keyframe offset to scene snapping Add a 0.5s keyframe offset to scene boundary snapping to avoid cutting into detected speech. SceneSnapService: introduce _KEYFRAME_INTERVAL and _apply_keyframe_offset to nudge start/end boundaries while respecting speech_start/speech_end; propagate speech_start/speech_end params through find_boundary_by_cut_offset and apply adjustments. Handlers: extract speech start/end in AdjustVideoClipHandler and pass them to the scene snap calls (also add pylint disable for too-many-locals), update scene command variants. SnapClipHandler: remove the user-facing snap success reply (now only logs the action). These changes prevent scene snaps from trimming into speech and tidy handler behavior. * Update snap tests and expected file hashes Replace simple success-message assertions in snap-related tests with file-match assertions (assert_command_result_file_matches) for /snap, /dopasuj and /sp commands. Update many media file hashes in bot/tests/expected_file_hashes.json to reflect regenerated files and add placeholder hashes for newly asserted snap outputs (snap_geniusz.mp4, snap_dopasuj_geniusz.mp4, snap_sp_geniusz.mp4) to be updated after the first test run. * Add AdjustBySceneHandler and refactor scene snap Introduce AdjustBySceneHandler to handle scene-based adjustments (/ds, /sdostosuj, /sadjust) and move scene-adjust logic out of AdjustVideoClipHandler. Wire the new handler into sending_videos package and SubscribedPermissionLevelFactory, and add unit tests for the new commands. Refactor SceneSnapService internals (private constants/methods, keyframe offset usage and type hints), remove an unnecessary try/except in transcription handler, and apply small cleanups (simplify JSON loading in snap handler, streamline compile_clips return, and adjust type hints in types.py). These changes separate responsibilities and tidy related code paths for scene snapping and clip adjustment. * Simplify SceneSnapService offset logic Remove speech_start/speech_end constraints from keyframe offset logic and simplify call sites. __apply_keyframe_offset now only takes (boundary, is_start) and always applies the fixed keyframe interval; find_boundary_by_cut_offset and callers were updated accordingly (including AdjustBySceneHandler) to stop passing speech timing. This refactors snapping behavior to be simpler and reduces parameter plumbing. * Rename '/ds' command to '/sd' in handler and tests Replace occurrences of the 'ds' command with 'sd': rename response functions (get_ds_* -> get_sd_*), update the invalid-args message to reference /sd, and adjust all tests to use /sd and expect output filenames prefixed with 'sd_'. Modified files: bot/responses/sending_videos/adjust_video_clip_handler_responses.py and bot/tests/sending_videos/test_adjust_by_scene.py. * Update expected_file_hashes.json * Rename response function usage to get_sd_* Update adjust_by_scene_handler to import and call the correctly named response helpers (get_sd_invalid_args_message, get_sd_no_scene_cuts_message) instead of the old get_ds_* names. This aligns the handler with the responses module and prevents NameError/invalid reference when replying with invalid-args or no-scene-cuts messages. * Check generated clip files and update hashes Change tests to assert generated clip files by hash instead of only checking messages, and update test fixtures accordingly. base_test.py: improve file-hash assertion to show expected vs received hash and a hint to update expected_file_hashes.json. sending_videos tests: capture command responses and call assert_command_result_file_matches for sd_geniusz.mp4 and snap_geniusz_no_adjust.mp4. expected_file_hashes.json: remove a stray filename from a hash entry and add/replace multiple snap/sd keys with PLACE_HOLDER values to reflect new expectations. * Update expected_file_hashes.json * Introduce SceneFinder and rename TranscriptionFinder Rename TranscriptionFinder to TextSegmentsFinder and update imports/usages across handlers and tests; add a new SceneFinder to centralize scene-cut queries and move scene-fetching logic out of SceneSnapService. Refactor SceneSnapService and handlers to call SceneFinder.fetch_scene_cuts. Simplify AdjustVideoClipHandler: use SegmentWithTimes types, return concrete segment structures, and split segment lookup into helper methods. Minor response formatting change for transcription output and add EXISTS key to ElasticsearchQueryKeys. * Remove automatic time extension in compiler Stop applying settings.EXTEND_BEFORE_COMPILE and EXTEND_AFTER_COMPILE when building clip start/end times. The import of settings was removed and the compiler now uses the segment's START_TIME and END_TIME directly (SceneSnapService still adjusts times when episode metadata is present). This prevents implicit time extension during compilation. * Update expected_file_hashes.json * Use compile-specific extends and clamp clip times Switch compile logic to use EXTEND_BEFORE_COMPILE / EXTEND_AFTER_COMPILE, set their defaults to 0, and apply them when computing clip boundaries. Also clamp start_time to >= 0.0 in ClipsCompiler to avoid negative timestamps and import settings where needed. These changes keep compile-time offsets separate from other extend settings and ensure valid clip ranges. * Add time formatter and simplify typings Introduce format_seconds_to_mmss() in utils and use it in transcription responses (replacing a local formatter). Remove an extra argument from argument-count validation in adjust_by_scene_handler and drop an unused import in adjust_video_clip_handler. Simplify several explicit typing annotations and query/dict declarations in text_segments_finder for cleaner, more consistent code style and minor whitespace/formatting tweaks. * Update VERSION * Standardize usage messages and add BotResponse Introduce a centralized BotResponse helper and standardize how handlers report usage errors. BotMessageHandler now requires _get_usage_message() and _validate_argument_count() no longer takes an explicit error_message — handlers were updated to implement _get_usage_message() and pass only min/max args. Many response functions were converted to BotResponse.* builders (info/warning/success/error/usage), new usage/error message helpers were added, and related handler validations were adjusted. Tests and handlers across administration, not_sending_videos and sending_videos were updated accordingly. * Update test expectation and regenerate hashes Change test_add_whitelist to expect an 'invalid args' message for an invalid user ID instead of 'no user id provided'. Regenerate and update expected_file_hashes.json to reflect updated file contents (several media/message hashes changed). * Add characters & emotions browsing features Introduce handlers, search, responses, types, and tests to support browsing characters and emotions. Adds CharactersHandler and EmotionsHandler, plus responses for formatting lists and scenes, and CharacterFinder (Elasticsearch queries) to fetch characters, scenes, and emotion labels. Updates constants and types (ActorKeys, EmotionKeys, CharacterScene, EmotionInfo, etc.), registers handlers in factories and package __init__, and adds tests and COMMANDS / start help text entries. Enables filtering by emotion, ignores season 0 entries, and sorts results by confidence/episode. * Add new search finders and refactor character finder Add dedicated search modules for episode names, sound events and video frames (episode_names_finder, sound_events_finder, video_frames_finder) to enable searching episodes, detected objects, and sound-event segments. Refactor CharacterFinder to query the video-frames index, use nested aggregations/filters for character appearances, add robust sorting by confidence and episode order, and simplify scene parsing to use frame timestamps. Update emotion keys in emotions_handler_responses to match new labels. Extend constants with ElasticsearchIndexSuffixes, new query key names, VideoFrameKeys and SoundEventKeys, and rename ActorKeys.ACTORS to "character_appearances" (breaking change for index field names). Overall changes improve accuracy and add new search capabilities for frames, sounds and episode metadata. * Parse handler args from raw message text Replace usage of self._message.get_args() with parsing via self._message.get_text().split()[1:] in CharactersHandler._do_handle. This ensures arguments are extracted consistently by splitting the raw message text and skipping the command token, avoiding issues with the previous get_args() behavior when handling character-related commands. * Add fuzzy character lookup & full-list export Enhance character handling: add fuzzy name matching and full-list export as text documents. Key changes: - CharactersHandler: add commands (/pl, postacie_lista), parse command to enable full export mode, resolve characters with fuzzy matching (difflib), and sanitize filenames. Added __send_document to write temp files and send them via responder. Support requesting full scene lists and full character lists. - Emotions/characters matching: use difflib to accept close emotion labels and fuzzy character queries. - Responses: introduce preview limits, nicer info/warning BotResponse formatting, preview vs full formatting functions (format_*_full), and include confidence/preview lines. - Tests: update expectations to reflect new output format and sorting behavior. These changes improve UX by accepting fuzzy input and allowing users to download full lists when needed. * Add character clip handler and response refactor Introduce CharacterClipHandler (commands: /klip_postac, /kp) to extract and send a single character clip: finds character (with optional emotion), snaps times, extracts clip, saves last_search/last_clip in DB and logs the action. Wire the new handler into sending_videos exports and subscribed permission factory. Refactor CharactersHandler to use CharacterFinder.find_best_matching_name, persist last search segments, and simplify character resolution logic. Add scene_to_search_segment helper and include video_path in parsed scenes in CharacterFinder. Revise character and emotion response formatting (emoji-based UI, convert_number_to_emoji usage, improved list/detail layouts) and add sending_videos response helpers. Small imports and utility adjustments to support these changes. * Adjust scene timing, formatting, and fuzzy match Add a _FRAME_SPAN_S constant and use it in scene_to_search_segment to expand the returned start/end around the scene timestamp (clamped to 0). Update format_characters_list and format_emotions_list to return BotResponse.info with simplified text bodies (removed emoji-heavy formatting). Lower difflib.get_close_matches cutoff from 0.6 to 0.5 to be more permissive when mapping emotion labels. Changes applied to characters_handler_responses.py and emotions_handler_responses.py. * Refactor character scenes response format Replace the old header construction with a compact count_line, append emotion filter to the count, and simplify hint strings (removed emoji and extra newlines). Assemble a body variable from the count_line, preview lines and hint, and return the response via BotResponse.info with an uppercase title. Keeps preview limit and uses convert_number_to_emoji for the scene count. * Parse optional emotion arg, Polish text fixes Add parse_character_args helper and refactor handlers to accept a variable number of args (use math.inf). Handlers now parse the last token as an optional emotion (using map_emotion_to_en) and reuse the new parser in characters and character-clip flows, removing duplicated emotion-mapping logic. Update many user-facing strings and formatting (Polish diacritics, improved list/scene layout, headers/emojis/code block) and adjust error/usage messages for clarity. Minor import and formatting cleanups in related response modules. * Add extra blank lines before full list hint Improve readability of the character scenes output by inserting additional blank lines before the "👉 Pełna lista" hint. The change adds the extra newlines in both branches of the conditional in characters_handler_responses.py so the hint is visually separated whether an emotion filter is present or not. * Add objects handler and ObjectFinder Introduce object browsing/search feature: add ObjectsHandler (bot/handlers/not_sending_videos/objects_handler.py) and its response templates (bot/responses/not_sending_videos/objects_handler_responses.py), plus ObjectFinder (bot/search/object_finder.py) to query Elasticsearch and group frames into scenes. Register the handler in the subscribed permission factory and package init. Update types (ObjectWithCount, ObjectScene, QuantityFilter) and constants (DetectedObjectKeys, SceneInfoKeys). Add tests for object commands and update user-facing docs/messages (COMMANDS.md, start handler responses). Feature supports /obiekt|/obj|/object to list objects or show scenes, with optional quantity filters (=, >, <, >=, <=). Logging and usage/error messages included. * Add full object list export, last-search storage Introduce full-list commands (/objl, /obj_lista) and full-document exports for objects and object scenes; add helpers to serialize scenes to search segments and pad single-frame scenes for previews. Persist last search into the database (segments saved as JSON) and implement sending temporary text documents with sanitized filenames. Adjust object aggregation to report episode_count (instead of frame_count) from Elasticsearch and update related types, responses, and UI hints. Misc: update commands/help text and small formatting tweaks. * Resolve object queries and use scene_count Add object name resolution and fuzzy/mapping lookup, switch from episode_count to scene_count, and surface a not-found message. - Add ObjectFinder.find_best_matching_object to normalize queries: exact match, Polish→English mapping, and difflib fuzzy matching. - Change ObjectFinder aggregations to count scenes (exclude season 0 frames, aggregate episodes then scenes) and produce scene_count per object. - Update responses to display scene_count and add get_object_not_found_message. - Add ObjectsHandler.__resolve_object_class to resolve queries and short-circuit when not found; use resolved class_name for searches. - Update types: ObjectWithCount now uses scene_count instead of episode_count. - Update tests to expect the new not-found message wording. These changes improve object lookup resilience for user queries and correct object statistics to reflect scenes instead of episodes. * Use doc_count for object scene counts Remove nested episode/scene aggregations and rely on reverse_nested doc_count to compute scene counts. Deleted _SCENE_NUMBER_FIELD, _EPISODES_AGG and _SCENES_AGG constants, added _DOC_COUNT, and simplified the bucket processing from an explicit loop/sum to a list comprehension that reads b[_BACK_TO_ROOT][_DOC_COUNT]. This simplifies the aggregation logic and reduces complexity of the Elasticsearch response handling. * Include season-0 frames and sort by object count object_finder: Stop skipping frames with season == 0 so frames from season 0 are included when grouping into scenes. video_frames_finder: Replace the previous season/episode/timestamp sort with a nested sort on the detected objects count for the requested object_class. Adds an object_count_field and uses a nested path+filter with mode=max and DESC order to prioritize frames with the most matching detected objects. * Use Polish names for displayed objects Switch object display to Polish labels by introducing an English->Polish mapping and a get_polish_name() helper. Update bot responses to call get_polish_name when formatting object lists and scenes. Also refine Polish->English canonicalizations (e.g. motorbike/aeroplane, diningtable, tvmonitor) and add many additional object mappings to improve lookup coverage. Import was added in responses to use the new helper. * Deduplicate overlapping video fragments Add logic to remove duplicate/overlapping scenes coming from the same video fragment. Import settings and introduce _clips_overlap (uses settings.EXTEND_BEFORE/EXTEND_AFTER) and _deduplicate_by_fragment which skips scenes without video_path and keeps the first non-overlapping scene per fragment. Integrate deduplication into ObjectFinder.get_all_objects after grouping frames to reduce redundant results. * Update character tests; remove unused method Remove unused _reply_error stub from CharacterHandlerMixin and update character tests to expect corrected Polish wording and a unified "not found" response for unknown emotions. Note: bot/tests/not_sending_videos/test_objects.py contains unresolved merge conflict markers around the has_scenes assignment (one side checks for _TEST_OBJECT, the other for the literal "osoba"); that conflict must be resolved. * Add character-not-found response and use it Introduce get_character_not_found_message in characters_handler_responses to centralize the 'no character found' warning. Replace the hardcoded Polish error string in character_handler_mixin with the new helper and adjust imports. Also extend test_list_whitelist by adding an additional UserProfile entry to cover the updated test scenario. * Add semantic search feature (vLLM + ES) Introduce semantic search functionality: add new /sens (aliases /meaning, /sen) command and handler that performs embedding-based searches. Implement VllmClient (aiohttp) to fetch text embeddings and new vllm-specific exceptions. Add SemanticSegmentsFinder to query Elasticsearch k-NN indices and deduplicate results, plus response formatters for text/frames/episodes. Update ElasticSearch mapping to include segment_id, start_time, end_time and video_path. Register the handler in factories and handlers init, expose new settings (VLLM host/model/timeout and ES index suffixes), add tests, and include aiohttp in requirements. * Add VLLM env vars to CI and docker-compose Add VLLM_HOST and VLLM_EMBEDDINGS_MODEL environment variables to the GitHub Actions workflow and docker-compose. The workflow now reads these values from secrets, and docker-compose provides defaults (VLLM_HOST=http://localhost:11435, VLLM_EMBEDDINGS_MODEL=qwen3vl-embed). This enables configuring an external VLLM server and embeddings model for both CI and local deployments. * Update VLLM host and video embeddings suffix Change default VLLM_HOST from http://localhost:8002 to http://localhost:11435 and rename ES_VIDEO_EMBEDDINGS_INDEX_SUFFIX from "video_embeddings" to "video_frames" to reflect frame-level embeddings indexing. No other logic changes. * Add semantic clip handler and frame normalization Introduce SemanticClipHandler to handle semantic clip queries (commands: sens_klip/senk/sk), including validation, VLLM error handling, DB logging of searches/clips, clipping via SceneSnapService/ClipsExtractor and sending video responses. Add corresponding user responses in semantic_clip_handler_responses.py and export the handler in sending_videos package; register it in the subscribed permission factory. Update SemanticSegmentsFinder to normalize frame results (populate START_TIME/END_TIME from scene_info) and use scene_number for frame deduplication, ensuring frame searches return consistent segment timestamps. * Refactor semantic handlers and search infra Introduce SemanticHandlerMixin to centralize semantic query parsing, validation and fetching (handles VLLM errors and length checks). Move VLLM/Elasticsearch clients into bot/search/infra and update imports accordingly. Consolidate common logic into BotMessageHandler (added _send_top_segment_as_clip, _send_document, _sanitize_filename) and remove duplicated document/sanitize helpers from character/object handlers. Switch character/object lookups to use video_frames_finder, delete legacy finders, and adapt many handlers and responses (including a new get_no_video_path_message and updated semantic frames formatting). Misc: limit search result sizes to settings.MAX_ES_RESULTS and avoid persisting EPISODE-mode semantic searches. * Consolidate document export and sanitize filenames Add centralized _send_text_as_document and _sanitize_for_filename to BotMessageHandler and update handlers to use them (characters, objects, search list), removing duplicated tempfile logic. Tighten argument count checks for characters/objects handlers. Refactor CompileClipsHandler clip-duration checker to a private method. Replace Polish<->English object mapping with bidict and a separate aliases map, adjust get_polish_name and lookup logic. Add bidict to requirements. Improve typing for scene_to_segment_dict using TypedDicts. Bump VERSION to 3.1.3 and minor whitespace/cleanup changes. * Add semantic modes, command aliases, clip trimming Introduce explicit semantic search modes and new command aliases, update handlers and docs, and enforce clip duration trimming. - Docs: expand COMMANDS.md and COMMANDSen.md with separate semantic search commands (text/frames/episode), new aliases (e.g. klipsens/klippostac/objlista), admin commands and other CLI refinements. - Handlers: add mode override parsing in SemanticHandlerMixin; SemanticSearchHandler recognizes frames/episode commands; rename/align command names (objects, character, semantic clip handlers) to match docs. - Clip limits: enforce max clip duration with admin hard-limit override; trim clips that exceed limit, notify user and log trimming events (imports/response messages updated accordingly). - Misc: bump VERSION to 4.0.0 and adjust related response imports/strings. * Move command constants into ObjectsHandler class Replace module-level _FULL_LIST_COMMANDS with private class-level lists (__SHORT_COMMANDS and __FULL_COMMANDS) on ObjectsHandler. Update get_commands to return the concatenation of these lists and adjust the is_full check to reference ObjectsHandler.__FULL_COMMANDS. This encapsulates command definitions within the handler class for better organization. * Check for command collisions in factory Add a pre-registration assertion in PermissionLevelFactory to detect duplicate Telegram commands across handler classes. create_and_register now calls __assert_no_command_collisions(), which instantiates each handler (message=None, responder=None, logger) to call get_commands() and raises a ValueError when the same command is provided by multiple handlers, naming both classes. Also import typing.Dict. * Add semantic mode parsing (default FRAMES) Implement _parse_semantic_mode_and_query in SemanticClipHandler to extract mode and query from the incoming message. Adds Tuple to typing imports and imports SemanticSearchMode; the new method splits the message text (skipping the command token) and returns SemanticSearchMode.FRAMES along with the joined remaining tokens as the query. * Introduce quick/long ES result limits Add MAX_ES_RESULTS_LONG and MAX_ES_RESULTS_QUICK settings and switch search code to use them. Updated semantic, sound, and video finders to default to the LONG limit for broader queries and added size params to CharacterFinder. Handlers now import settings and use either QUICK or LONG depending on context (e.g., quick UI searches use MAX_ES_RESULTS_QUICK). ClipHandler: import json, persist last search (json.dumps) to the database, normalize results list, and adjust the suggestion text. Object and semantic handlers cap returned segments to the QUICK limit. * Log computed hash and add placeholder hashes Add a debug log in bot/tests/base_test.py to emit the computed SHA-256 hash for the expected key to help troubleshoot test mismatches. Update bot/tests/expected_file_hashes.json to add placeholder entries for semantic_search_ucieczka.message and semantic_search_long_query_exceeds_limit.message (placeholder values) and adjust trailing comma for JSON formatting. * Update expected test file hashes Replace PLACEHOLDER values in bot/tests/expected_file_hashes.json with the actual SHA256 hashes for semantic_search_long_query_exceeds_limit.message and semantic_search_ucieczka.message so test fixtures reflect current file contents. * Refactor handler mixins into BotHandler subclasses Rename mixin modules to *_bot_handler and convert mixin classes into concrete BotMessageHandler subclasses. Update imports across character and semantic handlers to use the new classes, remove unused logging imports and pylint disables, tighten method visibility (make some semantic validators private), and add an abstract _handle_semantic_results hook. Adjust handler classes to inherit from the new BotHandler types so character/semantic handlers use the unified base implementation. * Switch to MarkdownV2 & escape message text Update Telegram responder to use MarkdownV2 for answer/edit_text/send_photo to match modern aiogram formatting and avoid parsing differences. Introduce markdown_decoration.quote escaping in multiple response generators (reindex, characters, objects, transcription) to safely quote user/variable content. Convert several plain-string responses into BotResponse objects (reindex completions, subscription status, search results, semantic embed warning) and adjust some message wording/escaping (e.g. angle-bracket hint, minor Polish text fixes). Bump aiogram dependency to ~=3.26.0. * Update expected hash for list_krowa.txt Update the expected checksum in bot/tests/expected_file_hashes.json for list_krowa.txt to the new value. This keeps the test fixtures in sync with the updated file content so hash-based validations pass. * Add characters & emotions browsing features Introduce handlers, search, responses, types, and tests to support browsing characters and emotions. Adds CharactersHandler and EmotionsHandler, plus responses for formatting lists and scenes, and CharacterFinder (Elasticsearch queries) to fetch characters, scenes, and emotion labels. Updates constants and types (ActorKeys, EmotionKeys, CharacterScene, EmotionInfo, etc.), registers handlers in factories and package __init__, and adds tests and COMMANDS / start help text entries. Enables filtering by emotion, ignores season 0 entries, and sorts results by confidence/episode. * Add new search finders and refactor character finder Add dedicated search modules for episode names, sound events and video frames (episode_names_finder, sound_events_finder, video_frames_finder) to enable searching episodes, detected objects, and sound-event segments. Refactor CharacterFinder to query the video-frames index, use nested aggregations/filters for character appearances, add robust sorting by confidence and episode order, and simplify scene parsing to use frame timestamps. Update emotion keys in emotions_handler_responses to match new labels. Extend constants with ElasticsearchIndexSuffixes, new query key names, VideoFrameKeys and SoundEventKeys, and rename ActorKeys.ACTORS to "character_appearances" (breaking change for index field names). Overall changes improve accuracy and add new search capabilities for frames, sounds and episode metadata. * Parse handler args from raw message text Replace usage of self._message.get_args() with parsing via self._message.get_text().split()[1:] in CharactersHandler._do_handle. This ensures arguments are extracted consistently by splitting the raw message text and skipping the command token, avoiding issues with the previous get_args() behavior when handling character-related commands. * Add fuzzy character lookup & full-list export Enhance character handling: add fuzzy name matching and full-list export as text documents. Key changes: - CharactersHandler: add commands (/pl, postacie_lista), parse command to enable full export mode, resolve characters with fuzzy matching (difflib), and sanitize filenames. Added __send_document to write temp files and send them via responder. Support requesting full scene lists and full character lists. - Emotions/characters matching: use difflib to accept close emotion labels and fuzzy character queries. - Responses: introduce preview limits, nicer info/warning BotResponse formatting, preview vs full formatting functions (format_*_full), and include confidence/preview lines. - Tests: update expectations to reflect new output format and sorting behavior. These changes improve UX by accepting fuzzy input and allowing users to download full lists when needed. * Add character clip handler and response refactor Introduce CharacterClipHandler (commands: /klip_postac, /kp) to extract and send a single character clip: finds character (with optional emotion), snaps times, extracts clip, saves last_search/last_clip in DB and logs the action. Wire the new handler into sending_videos exports and subscribed permission factory. Refactor CharactersHandler to use CharacterFinder.find_best_matching_name, persist last search segments, and simplify character resolution logic. Add scene_to_search_segment helper and include video_path in parsed scenes in CharacterFinder. Revise character and emotion response formatting (emoji-based UI, convert_number_to_emoji usage, improved list/detail layouts) and add sending_videos response helpers. Small imports and utility adjustments to support these changes. * Adjust scene timing, formatting, and fuzzy match Add a _FRAME_SPAN_S constant and use it in scene_to_search_segment to expand the returned start/end around the scene timestamp (clamped to 0). Update format_characters_list and format_emotions_list to return BotResponse.info with simplified text bodies (removed emoji-heavy formatting). Lower difflib.get_close_matches cutoff from 0.6 to 0.5 to be more permissive when mapping emotion labels. Changes applied to characters_handler_responses.py and emotions_handler_responses.py. * Refactor character scenes response format Replace the old header construction with a compact count_line, append emotion filter to the count, and simplify hint strings (removed emoji and extra newlines). Assemble a body variable from the count_line, preview lines and hint, and return the response via BotResponse.info with an uppercase title. Keeps preview limit and uses convert_number_to_emoji for the scene count. * Parse optional emotion arg, Polish text fixes Add parse_character_args helper and refactor handlers to accept a variable number of args (use math.inf). Handlers now parse the last token as an optional emotion (using map_emotion_to_en) and reuse the new parser in characters and character-clip flows, removing duplicated emotion-mapping logic. Update many user-facing strings and formatting (Polish diacritics, improved list/scene layout, headers/emojis/code block) and adjust error/usage messages for clarity. Minor import and formatting cleanups in related response modules. * Add extra blank lines before full list hint Improve readability of the character scenes output by inserting additional blank lines before the "👉 Pełna lista" hint. The change adds the extra newlines in both branches of the conditional in characters_handler_responses.py so the hint is visually separated whether an emotion filter is present or not. * Add character-not-found response and use it Introduce get_character_not_found_message in characters_handler_responses to centralize the 'no character found' warning. Replace the hardcoded Polish error string in character_handler_mixin with the new helper and adjust imports. Also extend test_list_whitelist by adding an additional UserProfile entry to cover the updated test scenario. * Refactor character lookup and emotion mapping Remove CharacterHandlerMixin and centralize character lookup in a new util (bot/utils/character_utils.py) exposing find_character which returns the matched character and parsed args. Update CharactersHandler and CharacterClipHandler to use the new helper, adjust command lists, and handle character-not-found responses in callers. Rename emotion mapping constants to _EMOTION_EN_TO_PL and _EMOTION_PL_TO_EN, simplify map_emotion_to_pl/map_emotion_to_en logic, and update tests. Add _extract_hits helper in character_finder to avoid repeating response parsing. Misc: adjust argument validation call in emotions handler and import small response helpers where needed. * Add ES extract helpers and responder text helper Introduce extract_hits and extract_sources helpers in elastic_search_manager and update search modules (character, episode, sound events, text segments, video frames) to use them for cleaner ES response handling. Add AbstractResponder.send_document_text to write string content to a temp file and send it, and refactor CharactersHandler to use this instead of an internal temp-file helper (removing the __send_document method). Replace emotion mapping dicts with a bidict-backed _EMOTIONS to simplify PL/EN lookups and update mapping logic and tests accordingly. Add bidict to requirements. * Remove type annotations from query vars Drop explicit `Dict[str, Any]` annotations on `query` variables across search finders. Replaced `query: Dict[str, Any] = {` with `query = {` in: - bot/search/character_finder.py - bot/search/episode_names_finder.py - bot/search/sound_events_finder.py - bot/search/video_frames_finder.py This is a non-functional cleanup to reduce verbosity and rely on type inference; no runtime behavior changes are expected. * Disable pylint duplicate-code; clean test file Add a module-level pylint disable for duplicate-code in bot/search/video_frames_finder.py to suppress false positives. Also remove an inline pylint disable (no-member) from bot/tests/base_test.py by cleaning up the line. * Update video_frames_finder.py * Add build_bool_must_query and refactor code Introduce build_bool_must_query in elastic_search_manager to construct common Elasticsearch bool queries (must clauses with optional filters). Refactor TextSegmentsFinder and VideoFramesFinder to use the new helper, replacing duplicated inline query dict constructions and updating imports. This reduces repetition and improves readability without changing functionality. * Delete character_handler_mixin.py * Delete character_handler_mixin.py.delete * Use shared scene_to_segment_dict helper Remove the local _scene_to_segment_dict implementation and import scene_to_segment_dict from bot.utils.functions. Update format_segment calls to use the shared helper and add the import. This centralizes segment conversion logic, reduces duplication, and preserves existing behavior. * Use shared scene_to_segment_dict, fix import Replace local _scene_to_segment_dict with the shared scene_to_segment_dict from bot.utils.functions and update calls in characters_handler_responses. Also correct the CharacterFinder import in bot/utils/character_utils.py to use bot.search.video_frames_finder (fixes module reference / centralizes segment formatting). * Update bot_message_handler.py * Make _get_usage_message return empty string by default * Support fuzzy series matching and nicer formatting Add find_matching_series utility and use it in SerialContextHandler to allow multi-word, underscored and fuzzy matches when selecting a series. Update handler to join remaining args into a query, resolve to the canonical series name, and use the matched name for persistence and logging. Improve response formatting by replacing underscores and using title-casing for displayed series names across success, error and current-series messages. Also add necessary imports (difflib, Optional) and minor command/whitespace fixes. * Allow unlimited args in SerialContextHandler Add math import and set max_args=math.inf in the SerialContextHandler's argument validation to explicitly allow an unlimited number of arguments for this handler. This makes the intent clear and prevents the validator from imposing an implicit upper bound. --------- Co-authored-by: Kamil <skelly37@protonmail.com>
[3.1.3] Objects search (/object command) (#142) * Add scene-snap feature and integrate handlers Introduce scene snap functionality and wire it across the bot. Adds SceneSnapService and new SnapClipHandler (plus responses and tests) to align clips to scene cuts; integrates snapping into transcription, clip selection, inline clips, adjust/compile flows and uses active_series where needed. Update Elasticsearch mappings to include segment/scene and embedding index mappings. Miscellaneous: update COMMANDS docs, make TelegramResponder.edit_text static, pass series_name into compilation, small type-hint/return cleanups and formatting fixes, and adjust rest_runner import to avoid protected-access issues. * Add typing and handle optional clip durations Introduce stronger typing across the codebase and add support for optional clip durations. Key changes: - Make VideoClip.duration Optional and propagate None-safety checks in handlers (bot_message_handler, compile_selected_clips_handler, inline_clip_handler) to avoid errors when duration is missing; display "?" for unknown durations. - Expand bot/types.py with concrete TypedDict definitions (SegmentWithScore, ElasticsearchSegment, ClipSegment, TranscriptionContext, and many others) and update imports throughout handlers and services. - Add/clarify type annotations for ES queries and responses, mp4_map (Dict[str, Path]) and SeriesScanner.__extract_episode_code (Optional[str]). - Use pathlib.Path where appropriate (snap_clip_handler, ClipsExtractor call) and minor refactors (return temp variable 'result' in compile_clips_handler). - Replace plain string constants with typed Final[str] constants in bot/utils/constants.py for improved type safety. These changes improve type safety, make edge cases (missing durations) explicit, and prevent runtime errors due to unexpected None or wrong types. * Use scene_start_time/scene_end_time fields Replace references to scene_info.start.seconds and scene_info.end.seconds with scene_info.scene_start_time and scene_info.scene_end_time. Update the Elasticsearch _source includes and the extraction logic so scene start/end times are read from the new flattened field names. Ensure index mappings or data are migrated to provide the new fields. * Apply keyframe offset to scene snapping Add a 0.5s keyframe offset to scene boundary snapping to avoid cutting into detected speech. SceneSnapService: introduce _KEYFRAME_INTERVAL and _apply_keyframe_offset to nudge start/end boundaries while respecting speech_start/speech_end; propagate speech_start/speech_end params through find_boundary_by_cut_offset and apply adjustments. Handlers: extract speech start/end in AdjustVideoClipHandler and pass them to the scene snap calls (also add pylint disable for too-many-locals), update scene command variants. SnapClipHandler: remove the user-facing snap success reply (now only logs the action). These changes prevent scene snaps from trimming into speech and tidy handler behavior. * Update snap tests and expected file hashes Replace simple success-message assertions in snap-related tests with file-match assertions (assert_command_result_file_matches) for /snap, /dopasuj and /sp commands. Update many media file hashes in bot/tests/expected_file_hashes.json to reflect regenerated files and add placeholder hashes for newly asserted snap outputs (snap_geniusz.mp4, snap_dopasuj_geniusz.mp4, snap_sp_geniusz.mp4) to be updated after the first test run. * Add AdjustBySceneHandler and refactor scene snap Introduce AdjustBySceneHandler to handle scene-based adjustments (/ds, /sdostosuj, /sadjust) and move scene-adjust logic out of AdjustVideoClipHandler. Wire the new handler into sending_videos package and SubscribedPermissionLevelFactory, and add unit tests for the new commands. Refactor SceneSnapService internals (private constants/methods, keyframe offset usage and type hints), remove an unnecessary try/except in transcription handler, and apply small cleanups (simplify JSON loading in snap handler, streamline compile_clips return, and adjust type hints in types.py). These changes separate responsibilities and tidy related code paths for scene snapping and clip adjustment. * Simplify SceneSnapService offset logic Remove speech_start/speech_end constraints from keyframe offset logic and simplify call sites. __apply_keyframe_offset now only takes (boundary, is_start) and always applies the fixed keyframe interval; find_boundary_by_cut_offset and callers were updated accordingly (including AdjustBySceneHandler) to stop passing speech timing. This refactors snapping behavior to be simpler and reduces parameter plumbing. * Rename '/ds' command to '/sd' in handler and tests Replace occurrences of the 'ds' command with 'sd': rename response functions (get_ds_* -> get_sd_*), update the invalid-args message to reference /sd, and adjust all tests to use /sd and expect output filenames prefixed with 'sd_'. Modified files: bot/responses/sending_videos/adjust_video_clip_handler_responses.py and bot/tests/sending_videos/test_adjust_by_scene.py. * Update expected_file_hashes.json * Rename response function usage to get_sd_* Update adjust_by_scene_handler to import and call the correctly named response helpers (get_sd_invalid_args_message, get_sd_no_scene_cuts_message) instead of the old get_ds_* names. This aligns the handler with the responses module and prevents NameError/invalid reference when replying with invalid-args or no-scene-cuts messages. * Check generated clip files and update hashes Change tests to assert generated clip files by hash instead of only checking messages, and update test fixtures accordingly. base_test.py: improve file-hash assertion to show expected vs received hash and a hint to update expected_file_hashes.json. sending_videos tests: capture command responses and call assert_command_result_file_matches for sd_geniusz.mp4 and snap_geniusz_no_adjust.mp4. expected_file_hashes.json: remove a stray filename from a hash entry and add/replace multiple snap/sd keys with PLACE_HOLDER values to reflect new expectations. * Update expected_file_hashes.json * Introduce SceneFinder and rename TranscriptionFinder Rename TranscriptionFinder to TextSegmentsFinder and update imports/usages across handlers and tests; add a new SceneFinder to centralize scene-cut queries and move scene-fetching logic out of SceneSnapService. Refactor SceneSnapService and handlers to call SceneFinder.fetch_scene_cuts. Simplify AdjustVideoClipHandler: use SegmentWithTimes types, return concrete segment structures, and split segment lookup into helper methods. Minor response formatting change for transcription output and add EXISTS key to ElasticsearchQueryKeys. * Remove automatic time extension in compiler Stop applying settings.EXTEND_BEFORE_COMPILE and EXTEND_AFTER_COMPILE when building clip start/end times. The import of settings was removed and the compiler now uses the segment's START_TIME and END_TIME directly (SceneSnapService still adjusts times when episode metadata is present). This prevents implicit time extension during compilation. * Update expected_file_hashes.json * Use compile-specific extends and clamp clip times Switch compile logic to use EXTEND_BEFORE_COMPILE / EXTEND_AFTER_COMPILE, set their defaults to 0, and apply them when computing clip boundaries. Also clamp start_time to >= 0.0 in ClipsCompiler to avoid negative timestamps and import settings where needed. These changes keep compile-time offsets separate from other extend settings and ensure valid clip ranges. * Add time formatter and simplify typings Introduce format_seconds_to_mmss() in utils and use it in transcription responses (replacing a local formatter). Remove an extra argument from argument-count validation in adjust_by_scene_handler and drop an unused import in adjust_video_clip_handler. Simplify several explicit typing annotations and query/dict declarations in text_segments_finder for cleaner, more consistent code style and minor whitespace/formatting tweaks. * Update VERSION * Standardize usage messages and add BotResponse Introduce a centralized BotResponse helper and standardize how handlers report usage errors. BotMessageHandler now requires _get_usage_message() and _validate_argument_count() no longer takes an explicit error_message — handlers were updated to implement _get_usage_message() and pass only min/max args. Many response functions were converted to BotResponse.* builders (info/warning/success/error/usage), new usage/error message helpers were added, and related handler validations were adjusted. Tests and handlers across administration, not_sending_videos and sending_videos were updated accordingly. * Update test expectation and regenerate hashes Change test_add_whitelist to expect an 'invalid args' message for an invalid user ID instead of 'no user id provided'. Regenerate and update expected_file_hashes.json to reflect updated file contents (several media/message hashes changed). * Add characters & emotions browsing features Introduce handlers, search, responses, types, and tests to support browsing characters and emotions. Adds CharactersHandler and EmotionsHandler, plus responses for formatting lists and scenes, and CharacterFinder (Elasticsearch queries) to fetch characters, scenes, and emotion labels. Updates constants and types (ActorKeys, EmotionKeys, CharacterScene, EmotionInfo, etc.), registers handlers in factories and package __init__, and adds tests and COMMANDS / start help text entries. Enables filtering by emotion, ignores season 0 entries, and sorts results by confidence/episode. * Add new search finders and refactor character finder Add dedicated search modules for episode names, sound events and video frames (episode_names_finder, sound_events_finder, video_frames_finder) to enable searching episodes, detected objects, and sound-event segments. Refactor CharacterFinder to query the video-frames index, use nested aggregations/filters for character appearances, add robust sorting by confidence and episode order, and simplify scene parsing to use frame timestamps. Update emotion keys in emotions_handler_responses to match new labels. Extend constants with ElasticsearchIndexSuffixes, new query key names, VideoFrameKeys and SoundEventKeys, and rename ActorKeys.ACTORS to "character_appearances" (breaking change for index field names). Overall changes improve accuracy and add new search capabilities for frames, sounds and episode metadata. * Parse handler args from raw message text Replace usage of self._message.get_args() with parsing via self._message.get_text().split()[1:] in CharactersHandler._do_handle. This ensures arguments are extracted consistently by splitting the raw message text and skipping the command token, avoiding issues with the previous get_args() behavior when handling character-related commands. * Add fuzzy character lookup & full-list export Enhance character handling: add fuzzy name matching and full-list export as text documents. Key changes: - CharactersHandler: add commands (/pl, postacie_lista), parse command to enable full export mode, resolve characters with fuzzy matching (difflib), and sanitize filenames. Added __send_document to write temp files and send them via responder. Support requesting full scene lists and full character lists. - Emotions/characters matching: use difflib to accept close emotion labels and fuzzy character queries. - Responses: introduce preview limits, nicer info/warning BotResponse formatting, preview vs full formatting functions (format_*_full), and include confidence/preview lines. - Tests: update expectations to reflect new output format and sorting behavior. These changes improve UX by accepting fuzzy input and allowing users to download full lists when needed. * Add character clip handler and response refactor Introduce CharacterClipHandler (commands: /klip_postac, /kp) to extract and send a single character clip: finds character (with optional emotion), snaps times, extracts clip, saves last_search/last_clip in DB and logs the action. Wire the new handler into sending_videos exports and subscribed permission factory. Refactor CharactersHandler to use CharacterFinder.find_best_matching_name, persist last search segments, and simplify character resolution logic. Add scene_to_search_segment helper and include video_path in parsed scenes in CharacterFinder. Revise character and emotion response formatting (emoji-based UI, convert_number_to_emoji usage, improved list/detail layouts) and add sending_videos response helpers. Small imports and utility adjustments to support these changes. * Adjust scene timing, formatting, and fuzzy match Add a _FRAME_SPAN_S constant and use it in scene_to_search_segment to expand the returned start/end around the scene timestamp (clamped to 0). Update format_characters_list and format_emotions_list to return BotResponse.info with simplified text bodies (removed emoji-heavy formatting). Lower difflib.get_close_matches cutoff from 0.6 to 0.5 to be more permissive when mapping emotion labels. Changes applied to characters_handler_responses.py and emotions_handler_responses.py. * Refactor character scenes response format Replace the old header construction with a compact count_line, append emotion filter to the count, and simplify hint strings (removed emoji and extra newlines). Assemble a body variable from the count_line, preview lines and hint, and return the response via BotResponse.info with an uppercase title. Keeps preview limit and uses convert_number_to_emoji for the scene count. * Parse optional emotion arg, Polish text fixes Add parse_character_args helper and refactor handlers to accept a variable number of args (use math.inf). Handlers now parse the last token as an optional emotion (using map_emotion_to_en) and reuse the new parser in characters and character-clip flows, removing duplicated emotion-mapping logic. Update many user-facing strings and formatting (Polish diacritics, improved list/scene layout, headers/emojis/code block) and adjust error/usage messages for clarity. Minor import and formatting cleanups in related response modules. * Add extra blank lines before full list hint Improve readability of the character scenes output by inserting additional blank lines before the "👉 Pełna lista" hint. The change adds the extra newlines in both branches of the conditional in characters_handler_responses.py so the hint is visually separated whether an emotion filter is present or not. * Add objects handler and ObjectFinder Introduce object browsing/search feature: add ObjectsHandler (bot/handlers/not_sending_videos/objects_handler.py) and its response templates (bot/responses/not_sending_videos/objects_handler_responses.py), plus ObjectFinder (bot/search/object_finder.py) to query Elasticsearch and group frames into scenes. Register the handler in the subscribed permission factory and package init. Update types (ObjectWithCount, ObjectScene, QuantityFilter) and constants (DetectedObjectKeys, SceneInfoKeys). Add tests for object commands and update user-facing docs/messages (COMMANDS.md, start handler responses). Feature supports /obiekt|/obj|/object to list objects or show scenes, with optional quantity filters (=, >, <, >=, <=). Logging and usage/error messages included. * Add full object list export, last-search storage Introduce full-list commands (/objl, /obj_lista) and full-document exports for objects and object scenes; add helpers to serialize scenes to search segments and pad single-frame scenes for previews. Persist last search into the database (segments saved as JSON) and implement sending temporary text documents with sanitized filenames. Adjust object aggregation to report episode_count (instead of frame_count) from Elasticsearch and update related types, responses, and UI hints. Misc: update commands/help text and small formatting tweaks. * Resolve object queries and use scene_count Add object name resolution and fuzzy/mapping lookup, switch from episode_count to scene_count, and surface a not-found message. - Add ObjectFinder.find_best_matching_object to normalize queries: exact match, Polish→English mapping, and difflib fuzzy matching. - Change ObjectFinder aggregations to count scenes (exclude season 0 frames, aggregate episodes then scenes) and produce scene_count per object. - Update responses to display scene_count and add get_object_not_found_message. - Add ObjectsHandler.__resolve_object_class to resolve queries and short-circuit when not found; use resolved class_name for searches. - Update types: ObjectWithCount now uses scene_count instead of episode_count. - Update tests to expect the new not-found message wording. These changes improve object lookup resilience for user queries and correct object statistics to reflect scenes instead of episodes. * Use doc_count for object scene counts Remove nested episode/scene aggregations and rely on reverse_nested doc_count to compute scene counts. Deleted _SCENE_NUMBER_FIELD, _EPISODES_AGG and _SCENES_AGG constants, added _DOC_COUNT, and simplified the bucket processing from an explicit loop/sum to a list comprehension that reads b[_BACK_TO_ROOT][_DOC_COUNT]. This simplifies the aggregation logic and reduces complexity of the Elasticsearch response handling. * Include season-0 frames and sort by object count object_finder: Stop skipping frames with season == 0 so frames from season 0 are included when grouping into scenes. video_frames_finder: Replace the previous season/episode/timestamp sort with a nested sort on the detected objects count for the requested object_class. Adds an object_count_field and uses a nested path+filter with mode=max and DESC order to prioritize frames with the most matching detected objects. * Use Polish names for displayed objects Switch object display to Polish labels by introducing an English->Polish mapping and a get_polish_name() helper. Update bot responses to call get_polish_name when formatting object lists and scenes. Also refine Polish->English canonicalizations (e.g. motorbike/aeroplane, diningtable, tvmonitor) and add many additional object mappings to improve lookup coverage. Import was added in responses to use the new helper. * Deduplicate overlapping video fragments Add logic to remove duplicate/overlapping scenes coming from the same video fragment. Import settings and introduce _clips_overlap (uses settings.EXTEND_BEFORE/EXTEND_AFTER) and _deduplicate_by_fragment which skips scenes without video_path and keeps the first non-overlapping scene per fragment. Integrate deduplication into ObjectFinder.get_all_objects after grouping frames to reduce redundant results. * Update character tests; remove unused method Remove unused _reply_error stub from CharacterHandlerMixin and update character tests to expect corrected Polish wording and a unified "not found" response for unknown emotions. Note: bot/tests/not_sending_videos/test_objects.py contains unresolved merge conflict markers around the has_scenes assignment (one side checks for _TEST_OBJECT, the other for the literal "osoba"); that conflict must be resolved. * Add character-not-found response and use it Introduce get_character_not_found_message in characters_handler_responses to centralize the 'no character found' warning. Replace the hardcoded Polish error string in character_handler_mixin with the new helper and adjust imports. Also extend test_list_whitelist by adding an additional UserProfile entry to cover the updated test scenario. * Consolidate document export and sanitize filenames Add centralized _send_text_as_document and _sanitize_for_filename to BotMessageHandler and update handlers to use them (characters, objects, search list), removing duplicated tempfile logic. Tighten argument count checks for characters/objects handlers. Refactor CompileClipsHandler clip-duration checker to a private method. Replace Polish<->English object mapping with bidict and a separate aliases map, adjust get_polish_name and lookup logic. Add bidict to requirements. Improve typing for scene_to_segment_dict using TypedDicts. Bump VERSION to 3.1.3 and minor whitespace/cleanup changes. * Add characters & emotions browsing features Introduce handlers, search, responses, types, and tests to support browsing characters and emotions. Adds CharactersHandler and EmotionsHandler, plus responses for formatting lists and scenes, and CharacterFinder (Elasticsearch queries) to fetch characters, scenes, and emotion labels. Updates constants and types (ActorKeys, EmotionKeys, CharacterScene, EmotionInfo, etc.), registers handlers in factories and package __init__, and adds tests and COMMANDS / start help text entries. Enables filtering by emotion, ignores season 0 entries, and sorts results by confidence/episode. * Add new search finders and refactor character finder Add dedicated search modules for episode names, sound events and video frames (episode_names_finder, sound_events_finder, video_frames_finder) to enable searching episodes, detected objects, and sound-event segments. Refactor CharacterFinder to query the video-frames index, use nested aggregations/filters for character appearances, add robust sorting by confidence and episode order, and simplify scene parsing to use frame timestamps. Update emotion keys in emotions_handler_responses to match new labels. Extend constants with ElasticsearchIndexSuffixes, new query key names, VideoFrameKeys and SoundEventKeys, and rename ActorKeys.ACTORS to "character_appearances" (breaking change for index field names). Overall changes improve accuracy and add new search capabilities for frames, sounds and episode metadata. * Parse handler args from raw message text Replace usage of self._message.get_args() with parsing via self._message.get_text().split()[1:] in CharactersHandler._do_handle. This ensures arguments are extracted consistently by splitting the raw message text and skipping the command token, avoiding issues with the previous get_args() behavior when handling character-related commands. * Add fuzzy character lookup & full-list export Enhance character handling: add fuzzy name matching and full-list export as text documents. Key changes: - CharactersHandler: add commands (/pl, postacie_lista), parse command to enable full export mode, resolve characters with fuzzy matching (difflib), and sanitize filenames. Added __send_document to write temp files and send them via responder. Support requesting full scene lists and full character lists. - Emotions/characters matching: use difflib to accept close emotion labels and fuzzy character queries. - Responses: introduce preview limits, nicer info/warning BotResponse formatting, preview vs full formatting functions (format_*_full), and include confidence/preview lines. - Tests: update expectations to reflect new output format and sorting behavior. These changes improve UX by accepting fuzzy input and allowing users to download full lists when needed. * Add character clip handler and response refactor Introduce CharacterClipHandler (commands: /klip_postac, /kp) to extract and send a single character clip: finds character (with optional emotion), snaps times, extracts clip, saves last_search/last_clip in DB and logs the action. Wire the new handler into sending_videos exports and subscribed permission factory. Refactor CharactersHandler to use CharacterFinder.find_best_matching_name, persist last search segments, and simplify character resolution logic. Add scene_to_search_segment helper and include video_path in parsed scenes in CharacterFinder. Revise character and emotion response formatting (emoji-based UI, convert_number_to_emoji usage, improved list/detail layouts) and add sending_videos response helpers. Small imports and utility adjustments to support these changes. * Adjust scene timing, formatting, and fuzzy match Add a _FRAME_SPAN_S constant and use it in scene_to_search_segment to expand the returned start/end around the scene timestamp (clamped to 0). Update format_characters_list and format_emotions_list to return BotResponse.info with simplified text bodies (removed emoji-heavy formatting). Lower difflib.get_close_matches cutoff from 0.6 to 0.5 to be more permissive when mapping emotion labels. Changes applied to characters_handler_responses.py and emotions_handler_responses.py. * Refactor character scenes response format Replace the old header construction with a compact count_line, append emotion filter to the count, and simplify hint strings (removed emoji and extra newlines). Assemble a body variable from the count_line, preview lines and hint, and return the response via BotResponse.info with an uppercase title. Keeps preview limit and uses convert_number_to_emoji for the scene count. * Parse optional emotion arg, Polish text fixes Add parse_character_args helper and refactor handlers to accept a variable number of args (use math.inf). Handlers now parse the last token as an optional emotion (using map_emotion_to_en) and reuse the new parser in characters and character-clip flows, removing duplicated emotion-mapping logic. Update many user-facing strings and formatting (Polish diacritics, improved list/scene layout, headers/emojis/code block) and adjust error/usage messages for clarity. Minor import and formatting cleanups in related response modules. * Add extra blank lines before full list hint Improve readability of the character scenes output by inserting additional blank lines before the "👉 Pełna lista" hint. The change adds the extra newlines in both branches of the conditional in characters_handler_responses.py so the hint is visually separated whether an emotion filter is present or not. * Add character-not-found response and use it Introduce get_character_not_found_message in characters_handler_responses to centralize the 'no character found' warning. Replace the hardcoded Polish error string in character_handler_mixin with the new helper and adjust imports. Also extend test_list_whitelist by adding an additional UserProfile entry to cover the updated test scenario. * Refactor character lookup and emotion mapping Remove CharacterHandlerMixin and centralize character lookup in a new util (bot/utils/character_utils.py) exposing find_character which returns the matched character and parsed args. Update CharactersHandler and CharacterClipHandler to use the new helper, adjust command lists, and handle character-not-found responses in callers. Rename emotion mapping constants to _EMOTION_EN_TO_PL and _EMOTION_PL_TO_EN, simplify map_emotion_to_pl/map_emotion_to_en logic, and update tests. Add _extract_hits helper in character_finder to avoid repeating response parsing. Misc: adjust argument validation call in emotions handler and import small response helpers where needed. * Add ES extract helpers and responder text helper Introduce extract_hits and extract_sources helpers in elastic_search_manager and update search modules (character, episode, sound events, text segments, video frames) to use them for cleaner ES response handling. Add AbstractResponder.send_document_text to write string content to a temp file and send it, and refactor CharactersHandler to use this instead of an internal temp-file helper (removing the __send_document method). Replace emotion mapping dicts with a bidict-backed _EMOTIONS to simplify PL/EN lookups and update mapping logic and tests accordingly. Add bidict to requirements. * Remove type annotations from query vars Drop explicit `Dict[str, Any]` annotations on `query` variables across search finders. Replaced `query: Dict[str, Any] = {` with `query = {` in: - bot/search/character_finder.py - bot/search/episode_names_finder.py - bot/search/sound_events_finder.py - bot/search/video_frames_finder.py This is a non-functional cleanup to reduce verbosity and rely on type inference; no runtime behavior changes are expected. * Disable pylint duplicate-code; clean test file Add a module-level pylint disable for duplicate-code in bot/search/video_frames_finder.py to suppress false positives. Also remove an inline pylint disable (no-member) from bot/tests/base_test.py by cleaning up the line. * Update video_frames_finder.py * Add build_bool_must_query and refactor code Introduce build_bool_must_query in elastic_search_manager to construct common Elasticsearch bool queries (must clauses with optional filters). Refactor TextSegmentsFinder and VideoFramesFinder to use the new helper, replacing duplicated inline query dict constructions and updating imports. This reduces repetition and improves readability without changing functionality. * Delete character_handler_mixin.py * Delete character_handler_mixin.py.delete * Use shared scene_to_segment_dict helper Remove the local _scene_to_segment_dict implementation and import scene_to_segment_dict from bot.utils.functions. Update format_segment calls to use the shared helper and add the import. This centralizes segment conversion logic, reduces duplication, and preserves existing behavior. * Make _get_usage_message return empty string by default
PreviousNext