### Start and Process User Requests with Neko.UserHandler Source: https://context7.com/shikimori/neko-achievements/llms.txt Starts a GenServer handler for a specific user to manage requests with backpressure and serialization. Processes user requests by calling the handler. The handler automatically terminates after a timeout when inactive. ```elixir {:ok, pid} = Neko.UserHandler.start_link(user_id) ``` ```elixir diff = Neko.UserHandler.process(%Neko.Request{ user_id: user_id, target_id: 1, status: "completed", action: "put" }) ``` -------------------------------- ### Neko Rule Structure Example Source: https://context7.com/shikimori/neko-achievements/llms.txt Illustrates the structure of a Neko rule, including ID, level, threshold, filters, and computed values like anime IDs and duration. This is a data structure example, not executable code. ```elixir %Neko.Rule{ neko_id: "animelist", level: 1, threshold: 15, filters: %{ "franchise" => "gintama", "genre_ids" => [1, 2], "not_anime_ids" => [123, 456] }, next_threshold: 50, anime_ids: MapSet.new([1, 2, 3]), duration: 12000 } ``` -------------------------------- ### Get All Anime Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all anime entries as a list. Useful for iterating through the entire anime database. ```elixir # Get all anime as a list animes = Neko.Anime.all() # Returns: [%Neko.Anime{}, ...] ``` -------------------------------- ### IEx Console: Get All Anime Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Fetches all anime data from the system, both as a list and as a map keyed by ID. Useful for quick data inspection. ```elixir Neko.Anime.all() Neko.Anime.all_by_id() ``` -------------------------------- ### Get All User Rates Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all watch status, episodes, and scores for a given user. Returns a map where keys are rate IDs. ```elixir # Get all rates for a user rates = Neko.UserRate.all(user_id) # Returns: Map with id => %Neko.UserRate{} entries ``` -------------------------------- ### Get All Duration Rules Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all configured duration-based achievement rules. Returns a `MapSet` of `Neko.Rule` structs. ```elixir # Get all duration rules rules = Neko.Rule.DurationRule.all() ``` -------------------------------- ### IEx Console: Get User's Anime IDs Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all anime IDs associated with a user's rates and stores them in a MapSet for efficient lookups and set operations. ```elixir user_anime_ids = user_id |> Neko.UserRate.all() |> Map.values() |> Enum.map(& &1.target_id) |> MapSet.new() ``` -------------------------------- ### IEx Console: Get Rules for neko_id Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all stored rules and filters them by a specific 'neko_id'. Useful for inspecting rule configurations in the IEx console. ```elixir Neko.Rule.CountRule.Store.all() |> Enum.filter(&(&1.neko_id == "animelist")) ``` -------------------------------- ### Get All User Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all currently stored achievements for a given user. Returns a MapSet of %Neko.Achievement{} structs. ```elixir # Get all achievements for a user achievements = Neko.Achievement.all(user_id) # Returns: MapSet of %Neko.Achievement{} structs ``` -------------------------------- ### Get All Count Rules Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all configured count-based achievement rules. Returns a `MapSet` of `Neko.Rule` structs. ```elixir # Get all count rules rules = Neko.Rule.CountRule.all() # Returns: MapSet of %Neko.Rule{} structs ``` -------------------------------- ### Get All Anime Indexed by ID Source: https://context7.com/shikimori/neko-achievements/llms.txt Retrieves all anime entries indexed by their ID. Provides efficient lookup of anime data using the anime's unique identifier. ```elixir # Get all anime indexed by id animes_by_id = Neko.Anime.all_by_id() # Returns: %{1 => %Neko.Anime{id: 1, ...}, 2 => %Neko.Anime{id: 2, ...}} ``` -------------------------------- ### Achievement Calculation Internal Flow Source: https://context7.com/shikimori/neko-achievements/llms.txt Outlines the internal steps taken by the Achievement Calculator: fetching rules, getting user rates, applying rules, and returning earned achievements. ```elixir # Internal flow: # 1. Fetches rules from CountRule.Store and DurationRule.Store # 2. Gets user's anime rates # 3. Applies each rule to determine earned achievements # 4. Returns MapSet of earned achievements ``` -------------------------------- ### Process User Rate Changes (Add Completed Anime) Source: https://context7.com/shikimori/neko-achievements/llms.txt Submit a POST request to this endpoint to record an anime as completed for a user. The system will then recalculate achievements. Requires user rate details and an Authorization header. ```bash # Add a completed anime to user's list curl -X POST "http://localhost:4000/user_rate" \ -H "Content-Type: application/json" \ -H "Authorization: your-token" \ -d '{ "id": 12345, "user_id": 919803, "target_id": 1, "status": "completed", "action": "put", "score": 8, "episodes": 12 }' ``` -------------------------------- ### Set All User Rates at Once Source: https://context7.com/shikimori/neko-achievements/llms.txt Replaces all existing user rates with a new list of `Neko.UserRate` structs. Use with caution as it overwrites current data. ```elixir # Set all rates at once Neko.UserRate.set(user_id, [ %Neko.UserRate{id: 1, user_id: user_id, target_id: 1, status: "completed"}, %Neko.UserRate{id: 2, user_id: user_id, target_id: 2, status: "watching", episodes: 5} ]) ``` -------------------------------- ### Fetch Anime Database from Shikimori API Source: https://context7.com/shikimori/neko-achievements/llms.txt Fetches the anime database from the Shikimori API. Returns a list of Neko.Anime structs. ```elixir animes = Neko.Shikimori.HTTPClient.get_animes!() # Returns: [%Neko.Anime{}, ...] ``` -------------------------------- ### IEx Console: Debug Rule Application Source: https://context7.com/shikimori/neko-achievements/llms.txt Prepares a map of user's anime data keyed by ID, which can be used for debugging the application of rules against specific user anime. ```elixir user_animes_by_id = Neko.Anime.all_by_id() |> Map.take(MapSet.to_list(user_anime_ids)) ``` -------------------------------- ### Load User Rates from Shikimori Source: https://context7.com/shikimori/neko-achievements/llms.txt Loads user anime rates, fetching from Shikimori if not cached. Use `reload/1` to force a refresh. ```elixir # Load user rates (fetches from Shikimori if not cached) Neko.UserRate.load(user_id) # Force reload from Shikimori Neko.UserRate.reload(user_id) ``` -------------------------------- ### Fetch User Rates from Shikimori API Source: https://context7.com/shikimori/neko-achievements/llms.txt Fetches a user's anime watching rates from the Shikimori API. Requires a user ID. Returns a list of Neko.UserRate structs. ```elixir user_rates = Neko.Shikimori.HTTPClient.get_user_rates!(user_id) # Returns: [%Neko.UserRate{}, ...] ``` -------------------------------- ### IEx Console: Load User Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Loads a specific user's data using their ID. This is a foundational step for debugging user-specific achievements and progress. ```elixir user_id = 919803 Neko.UserRate.load(user_id) ``` -------------------------------- ### Load User Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Fetches a user's achievements, loading them from Shikimori if they are not already cached. Use the user ID to retrieve the achievement data. ```elixir # Load achievements for a user (fetches from Shikimori if not cached) Neko.Achievement.load(user_id) ``` -------------------------------- ### Reset User Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Force a complete reload of a user's data from Shikimori and recalculate all achievements. Use the 'reset' action with the user ID. ```bash # Reset and reload user data curl -X POST "http://localhost:4000/user_rate" \ -H "Content-Type: application/json" \ -H "Authorization: your-token" \ -d '{ "user_id": 919803, "action": "reset" }' ``` -------------------------------- ### Neko.Achievement.Calculator Module Source: https://context7.com/shikimori/neko-achievements/llms.txt Calculates achievements for a user based on their anime rates and configured rules. ```APIDOC ## Neko.Achievement.Calculator Module ### Description This module is responsible for calculating a user's achievements based on their anime watching data and the defined achievement rules. It utilizes `poolboy` for managing worker processes to handle calculations efficiently. ### Functions - **`call(user_id)`** Calculates all achievements for a given user. - **Parameters**: - `user_id` (integer): The ID of the user for whom to calculate achievements. - **Returns**: - `MapSet` of `%Neko.Achievement{}` structs representing the earned achievements. ### Internal Flow 1. Fetches achievement rules from `CountRule.Store` and `DurationRule.Store`. 2. Retrieves the user's current anime watching rates. 3. Applies each defined rule to the user's data to determine which achievements have been earned. 4. Returns a `MapSet` containing all earned achievements. ``` -------------------------------- ### Process User Rate Changes (Delete Anime) Source: https://context7.com/shikimori/neko-achievements/llms.txt Send a POST request to remove an anime from a user's list and trigger achievement recalculation. Requires the rate ID, user ID, target ID, and action set to 'delete'. ```bash # Delete a user rate entry curl -X POST "http://localhost:4000/user_rate" \ -H "Content-Type: application/json" \ -H "Authorization: your-token" \ -d '{ "id": 12345, "user_id": 919803, "target_id": 1, "action": "delete" }' ``` -------------------------------- ### IEx Console: Find Matched and Missing Anime for a Rule Source: https://context7.com/shikimori/neko-achievements/llms.txt Compares a user's anime IDs against a rule's required anime IDs to find which anime match and which are missing. This helps in understanding rule completion status. ```elixir matched_anime_ids = MapSet.intersection(user_anime_ids, rule.anime_ids) missing_anime_ids = MapSet.difference(rule.anime_ids, user_anime_ids) |> MapSet.to_list() ``` -------------------------------- ### Health Check Request Source: https://context7.com/shikimori/neko-achievements/llms.txt Use this endpoint to verify if the Neko service is running. Requires an Authorization header. ```bash # Health check request curl -X GET "http://localhost:4000/ping" \ -H "Authorization: your-token" ``` -------------------------------- ### Fetch Achievements from Shikimori API Source: https://context7.com/shikimori/neko-achievements/llms.txt Fetches a user's achievements from the Shikimori API. Requires a user ID. Returns a list of Neko.Achievement structs. ```elixir achievements = Neko.Shikimori.HTTPClient.get_achievements!(user_id) # Returns: [%Neko.Achievement{}, ...] ``` -------------------------------- ### Calculate Duration Rule Value Source: https://context7.com/shikimori/neko-achievements/llms.txt Calculates the total watch time in minutes for a given duration rule, considering user rates and anime data. The calculation method differs based on user rate status ('completed' vs. 'watching'/'on_hold'). ```elixir # Calculate value for a duration rule # Returns: total minutes watched for matching anime # For "watching"/"on_hold": anime.duration * user_rate.episodes # For "completed": anime.total_duration value = Neko.Rule.DurationRule.value(rule, user_anime_ids, by_anime_id) ``` -------------------------------- ### Neko.Achievement Module Source: https://context7.com/shikimori/neko-achievements/llms.txt Manages user achievements storage and retrieval. ```APIDOC ## Neko.Achievement Module ### Description This module handles the persistence and retrieval of user achievements. Achievements are stored per-user using ETS (Erlang Term Storage) backed agent processes for efficient access. ### Functions - **`load(user_id)`** Loads achievements for a given user. If achievements are not cached, it fetches them from the Shikimori API. - **Parameters**: - `user_id` (integer): The ID of the user. - **Returns**: - `MapSet` of `%Neko.Achievement{}` structs. - **`reload(user_id)`** Forces a reload of a user's achievements directly from the Shikimori API, discarding any cached data. - **Parameters**: - `user_id` (integer): The ID of the user. - **`all(user_id)`** Retrieves all currently stored achievements for a user. - **Parameters**: - `user_id` (integer): The ID of the user. - **Returns**: - `MapSet` of `%Neko.Achievement{}` structs. - **`set(user_id, achievements)`** Sets or updates the achievements for a given user. - **Parameters**: - `user_id` (integer): The ID of the user. - `achievements` (MapSet): A MapSet containing `%Neko.Achievement{}` structs. - **`stop(user_id)`** Stops and cleans up the achievement storage process for a specific user. - **Parameters**: - `user_id` (integer): The ID of the user. ### Data Structures - **`%Neko.Achievement{}`** Represents a single achievement earned by a user. - **Fields**: - `user_id` (integer): The ID of the user who earned the achievement. - `neko_id` (string): The unique identifier for the achievement (e.g., "animelist"). - `level` (integer): The level of the achievement earned. - `progress` (integer): The progress made towards the next level of the achievement. ``` -------------------------------- ### Neko.UserRate Struct Definition Source: https://context7.com/shikimori/neko-achievements/llms.txt Defines the structure for user anime rates, including ID, user ID, target anime ID, score, status, and episodes watched. ```elixir %Neko.UserRate{ id: 12345, user_id: 919803, target_id: 1, # anime_id score: 8, status: "completed", episodes: 12 } ``` -------------------------------- ### IEx Console: Find a Specific Rule Source: https://context7.com/shikimori/neko-achievements/llms.txt Finds a specific rule by 'neko_id' and 'level'. This is useful for targeted debugging of achievement logic. ```elixir rule = Neko.Rule.CountRule.Store.all() |> Enum.filter(&(&1.neko_id == "animelist" && &1.level == 14)) |> Enum.at(0) ``` -------------------------------- ### Set User Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Manually sets the achievements for a user. This function expects a MapSet of %Neko.Achievement{} structs. ```elixir # Set achievements for a user Neko.Achievement.set(user_id, achievements) ``` -------------------------------- ### Reset User Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Forces a reload of all user data from Shikimori and recalculates achievements. ```APIDOC ## POST /user_rate (Reset Action) ### Description Forces a complete reload of a user's data from the Shikimori API and recalculates all their achievements. This is used when the `action` parameter is set to `reset`. ### Method POST ### Endpoint /user_rate ### Parameters #### Request Body - **user_id** (integer) - Required - The ID of the user whose data should be reset and recalculated. - **action** (string) - Required - Must be set to `"reset"`. ### Request Example ```json { "user_id": 919803, "action": "reset" } ``` ### Response #### Success Response (201) Typically returns an empty or minimal success response, as the primary action is the background recalculation. Specific achievement diffs might be returned depending on implementation details. #### Response Example (Example response may vary based on implementation, often indicates success without detailed diffs for a full reset) ```json { "message": "User data reset initiated for user_id: 919803" } ``` ``` -------------------------------- ### Neko.Rule.DurationRule API Source: https://context7.com/shikimori/neko-achievements/llms.txt Manages duration-based rules for triggering achievements based on the total watch time accumulated by users for anime matching specific filters. ```APIDOC ## Neko.Rule.DurationRule ### Description Duration-based rules that trigger achievements when users accumulate enough watch time (in minutes) for anime matching the rule's filters. ### Methods #### Get All Duration Rules - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Retrieves all duration-based achievement rules. - **Function**: `Neko.Rule.DurationRule.all()` - **Returns**: `MapSet` of `%Neko.Rule{}` structs. #### Set Duration Rules - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Sets new duration-based rules. - **Function**: `Neko.Rule.DurationRule.set(list_of_rules)` #### Calculate Duration Rule Value - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Calculates the value (total minutes watched) for a given duration rule. - **Function**: `Neko.Rule.DurationRule.value(rule, user_anime_ids, by_anime_id)` - **Returns**: Total minutes watched for matching anime. - For "watching"/"on_hold": `anime.duration * user_rate.episodes` - For "completed": `anime.total_duration` ### Rule Struct Example ```elixir %Neko.Rule{ neko_id: "gintama", level: 1, threshold: "80%", # percentage of total franchise duration duration: 12000, # total franchise duration in minutes filters: %{"franchise" => "gintama"} } %Neko.Rule{ neko_id: "code_geass", level: 1, threshold: 5000, # absolute minutes filters: %{"franchise" => "code_geass"} } ``` ``` -------------------------------- ### Neko.Achievement.Diff Module Source: https://context7.com/shikimori/neko-achievements/llms.txt Computes the difference between old and new achievement sets to determine what changed. ```APIDOC ## Neko.Achievement.Diff Module ### Description This module provides functionality to compare two sets of user achievements (an old set and a new set) and identify which achievements were added, removed, or updated. This is crucial for reporting changes after processing user rate updates. ### Functions - **`calculate(old_achievements, new_achievements)`** Compares two achievement sets and returns the differences. - **Parameters**: - `old_achievements` (MapSet): The previous set of achievements. - `new_achievements` (MapSet): The current set of achievements. - **Returns**: - A map containing keys `:added`, `:removed`, and `:updated`, each holding a list of changed achievements. ``` -------------------------------- ### YAML Rule Configuration: Count-based Achievement Levels Source: https://context7.com/shikimori/neko-achievements/llms.txt Defines rules for count-based achievements, including default settings and level-specific thresholds and metadata. Uses YAML anchors for inheritance. ```yaml # priv/rules/animelist.yml - Count-based achievement levels - &defaults neko_id: animelist level: 1 algo: count threshold: 15 metadata: title_ru: Добро пожаловать! title_en: Welcome to the world! text_en: You entered this world with light footsteps. image: /assets/achievements/anime/animelist_1.png border_color: "#e52b50" - <<: *defaults level: 2 threshold: 50 metadata: title_en: This is only the beginning image: /assets/achievements/anime/animelist_2.png - <<: *defaults level: 3 threshold: 100 metadata: title_en: My powers are growing! ``` -------------------------------- ### Neko Request Struct Definition Source: https://context7.com/shikimori/neko-achievements/llms.txt Defines the structure for incoming requests to the Neko API. Includes fields for user rate details and action type. ```elixir defmodule Neko.Request do defstruct ~w( id user_id target_id score action status episodes )a end ``` -------------------------------- ### Calculate Achievement Progress Source: https://context7.com/shikimori/neko-achievements/llms.txt Calculates the progress percentage towards the next achievement level. Handles cases for max level, just reaching the threshold, exceeding the next threshold, and partial progress. Requires rule configuration and current progress value. ```elixir Neko.Rule.Progress.progress(%{next_threshold: nil}, 100) # Returns: 100 ``` ```elixir Neko.Rule.Progress.progress(%{threshold: 15, next_threshold: 50}, 15) # Returns: 0 ``` ```elixir Neko.Rule.Progress.progress(%{threshold: 15, next_threshold: 50}, 50) # Returns: 100 ``` ```elixir Neko.Rule.Progress.progress(%{threshold: 15, next_threshold: 50}, 32) # Returns: 48.0 (floor of (32-15)/(50-15)*100) ``` -------------------------------- ### YAML Rule Configuration: Franchise-specific Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Defines rules for franchise-specific achievements, using 'duration' or 'count' algorithms and specifying filters like franchise and excluded anime IDs. Includes metadata for titles and images. ```yaml # priv/rules/_franchises.yml - Franchise-specific achievements - neko_id: gintama level: 1 algo: duration threshold: "80%" filters: franchise: gintama metadata: title_en: Gintama Master image: /assets/achievements/franchise/gintama.png - neko_id: naruto level: 1 algo: count threshold: "100%" filters: franchise: naruto not_anime_ids: - 1735 # exclude specific anime metadata: title_en: Naruto Completionist ``` -------------------------------- ### Calculate Achievement Set Differences Source: https://context7.com/shikimori/neko-achievements/llms.txt Compares two sets of achievements to identify added, removed, and updated achievements. Requires the `Neko.Achievement.Diff` module. ```elixir old_achievements = MapSet.new([ %Neko.Achievement{user_id: 1, neko_id: "animelist", level: 1, progress: 50} ]) new_achievements = MapSet.new([ %Neko.Achievement{user_id: 1, neko_id: "animelist", level: 1, progress: 100}, %Neko.Achievement{user_id: 1, neko_id: "animelist", level: 2, progress: 0} ]) diff = Neko.Achievement.Diff.call(old_achievements, new_achievements) ``` -------------------------------- ### Calculate Achievements for a Rule Module Source: https://context7.com/shikimori/neko-achievements/llms.txt Computes achievements for a specific user based on a given rule module, a set of rules, and the anime database. Returns a list of `Neko.Achievement` structs. ```elixir # Calculate achievements for a specific rule module achievements = Neko.Rule.achievements( Neko.Rule.CountRule, # rule module rules, # MapSet of rules animes_by_id, # anime database user_id # user to calculate for ) # Returns: list of %Neko.Achievement{} structs ``` -------------------------------- ### Process User Rate Changes Source: https://context7.com/shikimori/neko-achievements/llms.txt The main endpoint for processing anime rating changes. It accepts a JSON payload with user rate information and returns the achievement diff (added, removed, updated achievements). ```APIDOC ## POST /user_rate ### Description Processes changes to a user's anime ratings, triggering achievement recalculations. This endpoint handles adding, updating, or deleting anime from a user's list. ### Method POST ### Endpoint /user_rate ### Parameters #### Request Body - **id** (integer) - Required - The unique identifier for the user rate entry. - **user_id** (integer) - Required - The ID of the user whose rate is being changed. - **target_id** (integer) - Required - The ID of the anime being rated. - **status** (string) - Optional - The current status of the anime for the user (e.g., "completed", "watching"). Used when `action` is "put". - **action** (string) - Required - The action to perform. Valid values: "put", "delete", "reset", "noop". - **score** (integer) - Optional - The user's score for the anime (0-10). - **episodes** (integer) - Optional - The number of episodes watched. ### Request Example ```json { "id": 12345, "user_id": 919803, "target_id": 1, "status": "completed", "action": "put", "score": 8, "episodes": 12 } ``` ### Response #### Success Response (201) Returns an object detailing the achievements that were added, removed, or updated as a result of the rate change. - **added** (array) - A list of achievements added for the user. - **removed** (array) - A list of achievements removed for the user. - **updated** (array) - A list of achievements updated for the user. #### Response Example ```json { "added": [ { "user_id": 919803, "neko_id": "animelist", "level": 2, "progress": 0 } ], "removed": [], "updated": [ { "user_id": 919803, "neko_id": "animelist", "level": 1, "progress": 100 } ] } ``` ``` -------------------------------- ### Add or Update a User Rate Source: https://context7.com/shikimori/neko-achievements/llms.txt Adds a new user rate or updates an existing one. Requires a `Neko.UserRate` struct with at least `id`, `user_id`, and `target_id`. ```elixir # Add or update a rate Neko.UserRate.put(user_id, %Neko.UserRate{ id: 12345, user_id: user_id, target_id: 1, # anime_id status: "completed", score: 8, episodes: 12 }) ``` -------------------------------- ### Force Reload User Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Manually triggers a reload of a user's achievements directly from Shikimori, bypassing the cache. Requires the user ID. ```elixir # Force reload achievements from Shikimori Neko.Achievement.reload(user_id) ``` -------------------------------- ### Calculate User Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Initiates the calculation of all possible achievements for a given user based on their watching history and defined rules. Returns a MapSet of earned achievements. ```elixir # Calculate all achievements for a user achievements = Neko.Achievement.Calculator.call(user_id) # Returns: MapSet of %Neko.Achievement{} structs ``` -------------------------------- ### Neko.Rule.CountRule API Source: https://context7.com/shikimori/neko-achievements/llms.txt Manages count-based rules for triggering achievements based on the number of anime a user has completed that match specific filters. ```APIDOC ## Neko.Rule.CountRule ### Description Count-based rules that trigger achievements when users complete a certain number of anime matching the rule's filters. ### Methods #### Get All Count Rules - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Retrieves all count-based achievement rules. - **Function**: `Neko.Rule.CountRule.all()` - **Returns**: `MapSet` of `%Neko.Rule{}` structs. #### Reload Count Rules - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Reloads count-based rules from YAML files. - **Function**: `Neko.Rule.CountRule.reload()` #### Set Count Rules - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Sets new count-based rules, triggering a reload in all workers. - **Function**: `Neko.Rule.CountRule.set(list_of_rules)` #### Calculate Count Rule Value - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Calculates the value (number of matching anime completed) for a given count rule. - **Function**: `Neko.Rule.CountRule.value(rule, user_anime_ids, by_anime_id)` - **Returns**: Number of matching anime the user completed. ### Rule Struct Example ```elixir %Neko.Rule{ neko_id: "animelist", level: 1, threshold: 15, # absolute count filters: %{} } %Neko.Rule{ neko_id: "hikaru_no_go", level: 1, threshold: "100%", # percentage of franchise filters: %{"franchise" => "hikaru_no_go"} } ``` ``` -------------------------------- ### Neko.Rule Core Processing API Source: https://context7.com/shikimori/neko-achievements/llms.txt Provides core functionality for calculating achievements based on various rule types and user data. ```APIDOC ## Neko.Rule Core Processing ### Description Core rule processing module that calculates achievements from rules and user data. ### Methods #### Calculate Achievements for a Rule Module - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Calculates achievements for a specific rule module (e.g., CountRule, DurationRule). - **Function**: `Neko.Rule.achievements(rule_module, rules, animes_by_id, user_id)` - **Parameters**: - `rule_module`: The module containing the rules to process (e.g., `Neko.Rule.CountRule`). - `rules`: A `MapSet` of rules to apply. - `animes_by_id`: A map of anime data indexed by ID. - `user_id`: The ID of the user for whom to calculate achievements. - **Returns**: A list of `%Neko.Achievement{}` structs. #### Reload All Rules - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Reloads rules in all poolboy workers. - **Function**: `Neko.Rule.reload_all_rules()` ### Request Example (Calculate Achievements) ```elixir achievements = Neko.Rule.achievements( Neko.Rule.CountRule, # rule module rules, # MapSet of rules animes_by_id, # anime database user_id # user to calculate for ) ``` ``` -------------------------------- ### Neko.UserRate Management API Source: https://context7.com/shikimori/neko-achievements/llms.txt Manages user anime rates (watch status, episodes, scores) stored in ETS-backed agents. Allows loading, reloading, and manipulating user rate data. ```APIDOC ## Neko.UserRate Management ### Description Manages user anime rates (watch status, episodes, scores) stored in ETS-backed agents. Fetches data from Shikimori if not cached. ### Methods #### Load User Rates - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Loads user rates, fetching from Shikimori if not cached. - **Function**: `Neko.UserRate.load(user_id)` #### Force Reload User Rates - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Forces a reload of user rates from Shikimori. - **Function**: `Neko.UserRate.reload(user_id)` #### Get All User Rates - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Retrieves all rates for a given user. - **Function**: `Neko.UserRate.all(user_id)` - **Returns**: `Map` with `id => %Neko.UserRate{}` entries. #### Add or Update User Rate - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Adds a new user rate or updates an existing one. - **Function**: `Neko.UserRate.put(user_id, %Neko.UserRate{})` #### Delete User Rate - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Deletes a specific user rate. - **Function**: `Neko.UserRate.delete(user_id, user_rate)` #### Set All User Rates - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Sets all user rates at once, replacing existing ones. - **Function**: `Neko.UserRate.set(user_id, list_of_user_rates)` ### UserRate Struct - **id** (integer) - Unique identifier for the user rate. - **user_id** (integer) - The ID of the user. - **target_id** (integer) - The ID of the anime. - **score** (integer) - The score given by the user (0-10). - **status** (string) - The watch status (e.g., "completed", "watching"). - **episodes** (integer) - The number of episodes watched. ### Request Example (Put) ```elixir Neko.UserRate.put(user_id, %Neko.UserRate{ id: 12345, user_id: user_id, target_id: 1, # anime_id status: "completed", score: 8, episodes: 12 }) ``` ### Request Example (Set) ```elixir Neko.UserRate.set(user_id, [ %Neko.UserRate{id: 1, user_id: user_id, target_id: 1, status: "completed"}, %Neko.UserRate{id: 2, user_id: user_id, target_id: 2, status: "watching", episodes: 5} ]) ``` ``` -------------------------------- ### Calculate Count Rule Value Source: https://context7.com/shikimori/neko-achievements/llms.txt Calculates the value for a given count rule based on a user's anime IDs and the anime database. Returns the number of matching anime completed by the user. ```elixir # Calculate value for a count rule # Returns: number of matching anime the user completed value = Neko.Rule.CountRule.value(rule, user_anime_ids, by_anime_id) ``` -------------------------------- ### Set Anime Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Updates the anime database with a new list of anime entries. This operation also triggers a recalculation of associated rules. ```elixir # Set anime data (also recalculates rules) Neko.Anime.set([ %Neko.Anime{ id: 1, genre_ids: [1, 2, 3], genre_v2_ids: [10, 20], year: 2020, episodes: 12, duration: 24, # minutes per episode franchise: "gintama", total_duration: 288 # total minutes } ]) ``` -------------------------------- ### Reload Anime Data Source: https://context7.com/shikimori/neko-achievements/llms.txt Forces a reload of all anime data from Shikimori. This is a global operation affecting the entire system's anime cache. ```elixir # Reload anime data from Shikimori Neko.Anime.reload() ``` -------------------------------- ### Health Check Endpoint Source: https://context7.com/shikimori/neko-achievements/llms.txt Provides a simple health check to verify the service is running. ```APIDOC ## GET /ping ### Description Verifies if the Neko Achievements service is operational. ### Method GET ### Endpoint /ping ### Request Example ```bash curl -X GET "http://localhost:4000/ping" \ -H "Authorization: your-token" ``` ### Response #### Success Response (200) Returns `pong` if the service is running. #### Response Example ``` pong ``` ``` -------------------------------- ### Neko.Anime Management API Source: https://context7.com/shikimori/neko-achievements/llms.txt Manages the anime database used for achievement calculations. Anime data is loaded from Shikimori. ```APIDOC ## Neko.Anime Management ### Description Manages the anime database used for achievement calculations. Anime data is loaded from Shikimori. ### Methods #### Reload Anime Data - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Reloads anime data from Shikimori. - **Function**: `Neko.Anime.reload()` #### Get All Anime (List) - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Retrieves all anime as a list. - **Function**: `Neko.Anime.all()` - **Returns**: `[%Neko.Anime{}, ...]` #### Get All Anime (Indexed by ID) - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Retrieves all anime indexed by their ID. - **Function**: `Neko.Anime.all_by_id()` - **Returns**: `% {1 => %Neko.Anime{id: 1, ...}, 2 => %Neko.Anime{id: 2, ...}}` #### Set Anime Data - **Method**: N/A (Function Call) - **Endpoint**: N/A - **Description**: Sets anime data, which also triggers recalculation of rules. - **Function**: `Neko.Anime.set(list_of_animes)` ### Anime Struct Example ```elixir %Neko.Anime{ id: 1, genre_ids: [1, 2, 3], genre_v2_ids: [10, 20], year: 2020, episodes: 12, duration: 24, # minutes per episode franchise: "gintama", total_duration: 288 # total minutes } ``` ``` -------------------------------- ### Stop User Achievement Store Source: https://context7.com/shikimori/neko-achievements/llms.txt Cleans up and stops the GenServer process responsible for storing a specific user's achievements. Use the user ID to identify the process. ```elixir # Stop the achievement store (cleanup) Neko.Achievement.stop(user_id) ``` -------------------------------- ### Delete User Rate Source: https://context7.com/shikimori/neko-achievements/llms.txt Removes an anime from a user's list and recalculates achievements. ```APIDOC ## POST /user_rate (Delete Action) ### Description Removes an anime entry from a user's list, triggering an achievement recalculation. This is a specific use case of the `/user_rate` endpoint where the `action` is set to `delete`. ### Method POST ### Endpoint /user_rate ### Parameters #### Request Body - **id** (integer) - Required - The unique identifier for the user rate entry to be deleted. - **user_id** (integer) - Required - The ID of the user. - **target_id** (integer) - Required - The ID of the anime to remove from the user's list. - **action** (string) - Required - Must be set to `"delete"`. ### Request Example ```json { "id": 12345, "user_id": 919803, "target_id": 1, "action": "delete" } ``` ### Response #### Success Response (201) Returns an object detailing the achievements that were added, removed, or updated after the deletion. - **added** (array) - List of achievements added. - **removed** (array) - List of achievements removed. - **updated** (array) - List of achievements updated. #### Response Example ```json { "added": [], "removed": [ { "user_id": 919803, "neko_id": "animelist", "level": 1, "progress": 0 } ], "updated": [] } ``` ``` -------------------------------- ### Valid Status Values for Achievements Source: https://context7.com/shikimori/neko-achievements/llms.txt Lists the anime statuses that count towards achievements when using the 'put' action. Other statuses will result in the rate being removed. ```elixir # Valid status values for "put" action that count toward achievements: # "completed", "rewatching", "watching", "on_hold" # Other statuses like "planned" or "dropped" will remove the rate ``` -------------------------------- ### Neko Achievement Struct Source: https://context7.com/shikimori/neko-achievements/llms.txt Represents a single achievement awarded to a user. Includes user ID, achievement ID, level, and progress. ```elixir %Neko.Achievement{ user_id: 919803, neko_id: "animelist", level: 5, progress: 75 } ``` -------------------------------- ### Delete a User Rate Source: https://context7.com/shikimori/neko-achievements/llms.txt Removes a specific user rate entry. Requires the user ID and the `Neko.UserRate` struct to be deleted. ```elixir # Delete a rate Neko.UserRate.delete(user_id, user_rate) ``` -------------------------------- ### Achievement Set Difference Calculation Source: https://context7.com/shikimori/neko-achievements/llms.txt Calculates the difference between two sets of user achievements, identifying added, removed, and updated achievements. ```APIDOC ## Calculate diff between achievement sets ### Description Calculates the difference between two sets of user achievements. ### Method N/A (Function Call) ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```elixir old_achievements = MapSet.new([ %Neko.Achievement{user_id: 1, neko_id: "animelist", level: 1, progress: 50} ]) new_achievements = MapSet.new([ %Neko.Achievement{user_id: 1, neko_id: "animelist", level: 1, progress: 100}, %Neko.Achievement{user_id: 1, neko_id: "animelist", level: 2, progress: 0} ]) diff = Neko.Achievement.Diff.call(old_achievements, new_achievements) ``` ### Response #### Success Response (200) Returns a map containing added, removed, and updated achievements. - **added** (MapSet) - Achievements that are new. - **removed** (MapSet) - Achievements that have been removed. - **updated** (MapSet) - Achievements that have been modified. #### Response Example ```json { "added": MapSet<[%Neko.Achievement{level: 2, ...}]>, "removed": MapSet<[]>, "updated": MapSet<[%Neko.Achievement{level: 1, progress: 100, ...}]> } ``` ``` -------------------------------- ### Set Duration Rules Source: https://context7.com/shikimori/neko-achievements/llms.txt Updates the duration-based achievement rules with a new list. This operation affects how watch time achievements are calculated. ```elixir # Set new duration rules Neko.Rule.DurationRule.set([ %Neko.Rule{ neko_id: "gintama", level: 1, threshold: "80%", # percentage of total franchise duration duration: 12000, # total franchise duration in minutes filters: %{"franchise" => "gintama"} }, %Neko.Rule{ neko_id: "code_geass", level: 1, threshold: 5000, # absolute minutes filters: %{"franchise" => "code_geass"} } ]) ``` -------------------------------- ### Reload All Rules in Workers Source: https://context7.com/shikimori/neko-achievements/llms.txt Initiates a reload of all achievement rules across all poolboy workers in the system. This ensures consistency in rule processing. ```elixir # Reload rules in all poolboy workers Neko.Rule.reload_all_rules() ``` -------------------------------- ### Reload Count Rules Source: https://context7.com/shikimori/neko-achievements/llms.txt Reloads all count-based achievement rules from their YAML configuration files. This ensures the system uses the latest rule definitions. ```elixir # Reload rules from YAML files Neko.Rule.CountRule.reload() ``` -------------------------------- ### Set Count Rules Source: https://context7.com/shikimori/neko-achievements/llms.txt Updates the count-based achievement rules with a new list. This operation triggers a reload of rules across all system workers. ```elixir # Set new rules (triggers reload in all workers) Neko.Rule.CountRule.set([ %Neko.Rule{ neko_id: "animelist", level: 1, threshold: 15, # absolute count filters: %{} }, %Neko.Rule{ neko_id: "hikaru_no_go", level: 1, threshold: "100%", # percentage of franchise filters: %{"franchise" => "hikaru_no_go"} } ]) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.