# Table Of Contents * [Overview](#overview) * [Lifecycle](#lifecycle) * Window Components * [Range](#range) * [Count](#count) * [Duration](#duration) * [Duration String](#duration-string-recommended) * [Duration Object](#duration-object) * [Specifying Range](#specifying-range) * [Specifying Activity Type](#types-of-activities) * [Filters](#filters) * [Properties](#filter-properties) * [Lifecycle](#filter-lifecycle) * [`pre`](#pre-filter) * [`post`](#post-filter) * [More Examples](#more-examples) * [Count](#count-examples) * [Duration](#duration-examples) * [Count And Duration](#count-and-duration-examples) * [Activity Types](#activity-type-examples) * [Filter](#filter-examples) # Overview An **Activity Window** (`window`) is a group of properties that describe a **range** of [**Activities**](/docs/README.md#activity) to retrieve from Reddit and how to **filter** them. The main components of an Activity Window: * **Range** -- How many Activities ([`count`](#count)) or what time period ([`duration`](#duration)) of Activities to fetch * **Type of Activities** -- When **fetching** from an Author's history, should it return overview (any Activities), just Submissions, or just Comments? * **Filters** -- How the retrieved Activities should be [filtered](/docs/subreddit/components/README.md#filters) before returning them to a Rule As an example, if you want to run a **Recent Activity Rule** to check if a user has had activity in /r/mealtimevideos you also need to define what range of activities you want to look at from that user's history. ## Lifecycle Understanding the lifecycle for how CM uses the Activity Window to retrieve Activities is important to effectively using it. ```mermaid graph TD RangReq["Determine Range requirements (Parse `count` and `duration` values)"] --> EmptyList>Create empty Activities List] EmptyList --> Fetch>"Fetch Activities chunk (1 API Call)"] Fetch --> PreFilter>Invoke `pre` filter on Activities from chunk] PreFilter --> Add[Add filtered chunk to Activities list] Add --> CheckRange>Check Range Satisfaction] CheckRange -->|`count` Range| CountReq[Are there `count` # of Activities?] CheckRange -->|`duration` Range| DurationReq[Is the oldest Activity `duration` old?] CheckRange -->|`count` and `duration` Range| CountDurReq[Is either `count` and/or `duration` satisfied?] CountReq -->|No| Fetch DurationReq -->|No| Fetch CountDurReq -->|No| Fetch CountReq -->|Yes| FetchDone DurationReq -->|Yes| FetchDone CountDurReq -->|Yes| FetchDone FetchDone[Fetch Complete] --> PostFilter>Invoke `post` filter on all Activities] PostFilter --> Return>Return Activities to Rule] click PreFilter href "#pre-filter" click PostFilter href "#post-filter" click CountReq href "#count" click DurationReq href "#duration" click CountDurReq href "#specifying-range" ``` # Range There are two types of values that can be used when defining a range: ## Count This is the **number** of activities you want to retrieve. It's straightforward -- if you want to look at the last 100 activities for a user you can use `100` as the value. ### Shorthand If **count** is the only property you want to define (leaving everything else as default) then `window` can be defined with just this value: ```yaml window: 70 # retrieve 70 activities ``` **** Otherwise, use the `count` property on a full `window` object: ```yaml window: count: 70 # retrieve 70 activities ... ``` ## Duration A **duration of time** between which all activities will be retrieved. This is a **relative value** that calculates the actual range based on **the duration of time subtracted from when the rule is run.** For example: * Today is **July 15th** * You define a duration of **10 days** Then the range of activities to be retrieved will be between **July 5th and July 15th** (10 days). ### Shorthand If a Duration string is the only property you want to define (leaving everything else as default) then `window` can be defined with just this value: ```yaml window: '9 days' # retrieve last 9 days of activities ``` Otherwise, use the `duration` property on a full `window` object: ```yaml window: duration: '9 days' ... ``` ### Duration Types The value used to define the duration can be **one of these two types**: #### Duration String (recommended) A string consisting of * A [Dayjs unit of time](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) * The value of that unit of time Examples: * `9 days` * `14 hours` * `80 seconds` You can ensure your string is valid by testing it [here.](https://regexr.com/61em3) ##### As ISO 8601 duration string If you're a real nerd you can also use a [standard duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) string. Examples * `PT15M` (15 minutes) Ensure your string is valid by testing it [here.](https://regexr.com/61em9) #### Duration Object If you need to specify multiple units of time for your duration you can instead provide a [Dayjs duration **object**](https://day.js.org/docs/en/durations/creating#list-of-all-available-units) consisting of Dayjs unit-values. ```yaml window: duration: days: 4 hours: 6 minutes: 20 ``` ## Specifying Range You may use **one or both range properties.** If both range properties are specified then the value `satisfyOn` determines how the final range is determined ### Using `satisfyOn: any` (default) If **any** then Activities will be retrieved until one of the range properties is met, **whichever occurs first.** ```yaml window: count: 80 duration: 90 days satisfyOn: any ``` Activities are retrieved in chunks of 100 (or `count`, whichever is smaller) * If 90 days of activities returns only 40 activities => returns 40 activities * If 80 activities is only 20 days of range => 80 activities ### Using `satisfyOn: all` If **all** then both ranges must be satisfied. Effectively, whichever range produces the most Activities will be the one that is used. ```yaml window: count: 100 duration: 90 days satisfyOn: all ``` Activities are retrieved in chunks of 100 (or `count`, whichever is smaller) * If at 90 days of activities => 40 activities retrieved * continue retrieving results until 100 activities * so range is >90 days of activities * If at 100 activities => 20 days of activities retrieved * continue retrieving results until 90 days of range * so results in >100 activities # Types of Activities When retrieving an Author's history the window can specify if it should return all Activities (overview), just Comments, or just Submissions, using the `fetch` property: ```yaml window: # defaults to 'overview' fetch: 'submission' # must be one of: overview, comment, submission ``` # Filters Activity Window can also specify [Item and Subreddit Filters](/docs/subreddit/components/README.md#filters) to filter the Activities retrieved from Reddit before they are returned to a Rule. Activities can be filtered **during** (`pre`) retrieval or **after** (`post`) retrieval. **When**, during the window **lifecycle**, the Activities are filtered can change the set of Activities returned to a Rule drastically. ## Filter Properties Regardless of when you are filtering Activities the shape of the filter is the same. Filter properties: * `subreddits` -- A [Filter Shape](/docs/subreddit/components/README.md#filter-shapes) for filtering by the [Subreddit Criteria](/docs/subreddit/components/README.md#subreddit-filter) of each Activity * `submissionState` -- A [Filter Shape](/docs/subreddit/components/README.md#filter-shapes) for [Submission Criteria](/docs/subreddit/components/README.md#item-filter). Will run only if filtering a Submission. * `commentState` -- A [Filter Shape](/docs/subreddit/components/README.md#filter-shapes) for [Comment Criteria](/docs/subreddit/components/README.md#item-filter). Will run only if filtering a Comment. * `activityState` -- A [Filter Shape](/docs/subreddit/components/README.md#filter-shapes) for either [Submission or Comment Criteria](/docs/subreddit/components/README.md#item-filter). Will run only if `submissionState` or `commentState` is not defined for their respective Activity types. In this example the filter only returns Activities: * With a subreddit that * Is from the subreddit /r/mealtimevideos OR * Has a name that matches the regex `/ask.*/i` (starts with `ask`) OR * Is marked as NSFW * AND if the Activity is a Submission: * must be self text * AND if the Activity is a Comment (because `activityState` is defined and `commentState` is not): * Comment is NOT removed ```yaml subreddits: include: - 'mealtimevideos' - '/ask.*/i' - over18: true submissionState: include: - is_self: true activityState: exclude: - removed: false ``` ## Filter Lifecycle Filters can be independently specified for two different "locations" during the window lifecycle using the `filterOn` property. ### `pre` Filter `pre` will filter Activities from **each API call, before** they are added to the full list of retrieved Activities and, most importantly, **before CM checks if Range conditions have been satisfied.** See the [Lifecycle Diagram](#lifecycle). This is useful when you want the Range conditions to be applied to an "already filtered" list of Activities -- as opposed to afterwards (like `post`). #### `max` restriction However, `pre` introduces the possibility of **near impossible range conditions.** For example, if you want 200 activities from a subreddit a user has never created activities in then CM would fetch the user's **entire history** before finishing since each chunk of Activities would be filtered to 0. To prevent this scenario all `pre` filters must also specify a `max` [range](#range) that tell CM: > if X range of **non-filtered** Activities is reached stop immediately #### `pre` Example Let's follow an example scenario where you want the last 200 activities a user has in /r/mealtimevideos ```yaml window: count: 200 filterOn: pre: subreddits: include: - mealtimevideos max: 400 ``` * CM fetches the first chunk of 100 Activities (100 total unfiltered) * `pre` Filter finds 70 of those 100 are from /r/mealtimevideos => Total Filtered Activities 70 * CM Checks range condition => 70 is less than 200 * CM Checks max condition => 100 unfiltered is less than 400 * CM fetches second chunk of 100 Activities (200 total unfiltered) * `pre` Filter finds another 70 of those 100 are from /r/mealtimevideos => Total Filtered Activities 140 * CM Checks range condition => 140 is less than 200 * CM Checks max condition => 200 unfiltered is less than 400 * CM fetches third chunk of 100 activities (300 total unfiltered) * `pre` Filter finds 90 of those 100 are from /r/mealtimevideos => Total Filtered Activities 230 * CM checks range condition => 230 is more than 200 * CM Checks max condition => 300 unfiltered is less than 400 **CM is done fetching activities with 230 Filtered Activities** Now let's look at the same scenario but `max` is hit: * CM fetches the first chunk of 100 Activities (100 total unfiltered) * `pre` Filter finds 10 of those 100 are from /r/mealtimevideos => Total Filtered Activities 10 * CM Checks range condition => 10 is less than 200 * CM Checks max condition => 100 unfiltered is less than 400 * CM fetches second chunk of 100 Activities (200 total unfiltered) * `pre` Filter finds another 15 of those 100 are from /r/mealtimevideos => Total Filtered Activities 25 * CM Checks range condition => 25 is less than 200 * CM Checks max condition => 200 unfiltered is less than 400 * CM fetches third chunk of 100 activities (300 total unfiltered) * `pre` Filter finds 5 of those 100 are from /r/mealtimevideos => Total Filtered Activities 30 * CM checks range condition => 30 is less than 200 * CM Checks max condition => 300 unfiltered is less than 400 * CM fetches fourth chunk of 100 activities (400 total unfiltered) * `pre` Filter finds 0 of those 100 are from /r/mealtimevideos => Total Filtered Activities 30 * CM checks range condition => 30 is less than 200 * CM Checks max condition => 400 unfiltered is NOT less than 400 * CM stops fetching due to max condition hit **CM is done fetching activities with 30 Filtered Activities** ### `post` Filter `post` will filter the full list of Activities **after range conditions are satisfied**. This also means it receives the list of Activities filtered by the [`pre` filter](#pre-filter), if one is defined. The list returned by `post` is what the Rule receives. #### Example Let's follow an example scenario where you want to get the last 200 activities from a User's history **and then** return any of those 200 that were made in /r/mealtimevideos -- contrast this wording to the [`pre` example](#pre-example) ```yaml window: count: 200 filterOn: post: subreddits: include: - mealtimevideos ``` * CM fetches the first chunk of 100 Activities (100 total unfiltered) * CM checks range condition => 100 is less than 200 * CM fetches the second chunk of 100 Activities (200 total unfiltered) * CM checks range condition => 200 is equal to 200 * CM is done fetching activities with 200 unfiltered Activities * `post` filter finds 10 of those 200 are from /r/mealtimevideos * CM returns 10 Activities to the Rule # More Examples ### Count Examples #### Get last 100 activities in a User's history ```yaml window: 100 ``` or ```yaml window: count: 100 ``` ### Duration Examples #### Get last 2 weeks of a User's history ```yaml window: '2 weeks' ``` or ```yaml window: duration: '2 weeks' ``` #### Get last 24 hours and 30 minutes of User's history ```yaml window: duration: hours: 24 minutes: 30 ``` ### Count and Duration Examples #### Get EITHER last 6 months or last 200 activities of User's history Whichever is [satisifed first](#using-satisfyon-any-default) ```yaml window: count: 200 duration: '6 months' ``` #### Get AT LEAST the last 6 months and last 200 activities of User's history Both must be [satisifed](#using-satisfyon-all) ```yaml window: count: 200 duration: '6 months' satisfyOn: 'all' ``` ### Activity Type Examples #### Get the last 200 submissions from User's history ```yaml window: count: 200 fetch: 'submission' ``` #### Get the last 200 comments from User's history ```yaml window: count: 200 fetch: 'comment' ``` #### Get the last 200 activities (submissions or comments) from User's history ```yaml window: count: 200 fetch: 'overview' # or do not include 'fetch' at all, this is the default ``` ### Filter Examples #### Get the all activities from NSFW subreddits in the User's last 200 activities ```yaml window: count: 200 filterOn: post: subreddits: include: - over18: true ``` #### Get the all comments from NSFW subreddits where user is OP, in the User's last 200 activities ```yaml window: count: 200 fetch: 'comment' filterOn: post: subreddits: include: - over18: true commentState: include: - op: true ``` #### Get the last 200 self-text submissions from a User's history and return any from ask* subreddits ```yaml window: count: 200 fetch: 'submission' filterOn: pre: submissionState: include: - is_self: true max: 500 post: subreddits: include: - '/ask.*/i' ```