Files
context-mod/docs/subreddit/activitiesWindow.md
2022-05-12 12:22:48 -04:00

492 lines
15 KiB
Markdown

# 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'
```