mirror of
https://github.com/FoxxMD/context-mod.git
synced 2026-05-11 03:00:42 -04:00
Refactor window criteria to actually work as described
This commit is contained in:
@@ -118,7 +118,6 @@ export class Check implements ICheck {
|
||||
this.logger.info(`❌ => Item did not pass 'itemIs' test`);
|
||||
return [false, allResults];
|
||||
}
|
||||
debugger;
|
||||
let authorPass = null;
|
||||
if (this.authorIs.include !== undefined && this.authorIs.include.length > 0) {
|
||||
for (const auth of this.authorIs.include) {
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
* @pattern ^(-?)P(?=\d|T\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)([DW]))?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$
|
||||
* */
|
||||
export type ISO8601 = string;
|
||||
export type ActivityWindowType = Duration | number | ActivityWindowCriteria;
|
||||
export type Duration = ISO8601 | DurationObject;
|
||||
export type ActivityWindowType = DurationVal | number | ActivityWindowCriteria;
|
||||
export type DurationVal = ISO8601 | DurationObject;
|
||||
|
||||
/**
|
||||
* If both properties are defined then the first criteria met will be used IE if # of activities = count before duration is reached then count will be used, or vice versa
|
||||
* If both criteria are defined then whichever is met first will be used
|
||||
*
|
||||
* EX 100 count, 90 days
|
||||
*
|
||||
* * If 90 days of activities = 40 activities => returns 40 activities
|
||||
* * If 100 activities is only 20 days => 100 activities
|
||||
* @minProperties 1
|
||||
* @additionalProperties false
|
||||
* */
|
||||
@@ -18,7 +23,7 @@ export interface ActivityWindowCriteria {
|
||||
* */
|
||||
count?: number,
|
||||
/**
|
||||
* An ISO 8601 duration or Day.js duration object.
|
||||
* An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) or [Day.js duration object](https://day.js.org/docs/en/durations/creating).
|
||||
*
|
||||
* The duration will be subtracted from the time when the rule is run to create a time range like this:
|
||||
*
|
||||
@@ -27,7 +32,7 @@ export interface ActivityWindowCriteria {
|
||||
* EX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM
|
||||
* @examples ["PT1M", {"minutes": 15}]
|
||||
* */
|
||||
duration?: Duration
|
||||
duration?: DurationVal
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"definitions": {
|
||||
"ActivityWindowCriteria": {
|
||||
"additionalProperties": false,
|
||||
"description": "If both properties are defined then the first criteria met will be used IE if # of activities = count before duration is reached then count will be used, or vice versa",
|
||||
"description": "If both criteria are defined then whichever is met first will be used\n\nEX 100 count, 90 days\n\n* If 90 days of activities = 40 activities => returns 40 activities\n* If 100 activities is only 20 days => 100 activities",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
@@ -22,7 +22,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"description": "An ISO 8601 duration or Day.js duration object.\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"description": "An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) or [Day.js duration object](https://day.js.org/docs/en/durations/creating).\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"examples": [
|
||||
"PT1M",
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"definitions": {
|
||||
"ActivityWindowCriteria": {
|
||||
"additionalProperties": false,
|
||||
"description": "If both properties are defined then the first criteria met will be used IE if # of activities = count before duration is reached then count will be used, or vice versa",
|
||||
"description": "If both criteria are defined then whichever is met first will be used\n\nEX 100 count, 90 days\n\n* If 90 days of activities = 40 activities => returns 40 activities\n* If 100 activities is only 20 days => 100 activities",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
@@ -42,7 +42,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"description": "An ISO 8601 duration or Day.js duration object.\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"description": "An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) or [Day.js duration object](https://day.js.org/docs/en/durations/creating).\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"examples": [
|
||||
"PT1M",
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"definitions": {
|
||||
"ActivityWindowCriteria": {
|
||||
"additionalProperties": false,
|
||||
"description": "If both properties are defined then the first criteria met will be used IE if # of activities = count before duration is reached then count will be used, or vice versa",
|
||||
"description": "If both criteria are defined then whichever is met first will be used\n\nEX 100 count, 90 days\n\n* If 90 days of activities = 40 activities => returns 40 activities\n* If 100 activities is only 20 days => 100 activities",
|
||||
"minProperties": 1,
|
||||
"properties": {
|
||||
"count": {
|
||||
@@ -22,7 +22,7 @@
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"description": "An ISO 8601 duration or Day.js duration object.\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"description": "An [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations) or [Day.js duration object](https://day.js.org/docs/en/durations/creating).\n\nThe duration will be subtracted from the time when the rule is run to create a time range like this:\n\nendTime = NOW <----> startTime = (NOW - duration)\n\nEX endTime = 3:00PM <----> startTime = (NOW - 15 minutes) = 2:45PM -- so look for activities between 2:45PM and 3:00PM",
|
||||
"examples": [
|
||||
"PT1M",
|
||||
{
|
||||
|
||||
@@ -5,8 +5,14 @@ import dayjs, {Dayjs} from "dayjs";
|
||||
import Mustache from "mustache";
|
||||
import he from "he";
|
||||
import {AuthorCriteria, RuleResult, UserNoteCriteria} from "../Rule";
|
||||
import {ActivityWindowType, CommentState, SubmissionState, TypedActivityStates} from "../Common/interfaces";
|
||||
import {normalizeName, truncateStringToLength} from "../util";
|
||||
import {
|
||||
ActivityWindowType,
|
||||
CommentState,
|
||||
DurationVal,
|
||||
SubmissionState,
|
||||
TypedActivityStates
|
||||
} from "../Common/interfaces";
|
||||
import {isActivityWindowCriteria, normalizeName, truncateStringToLength} from "../util";
|
||||
import UserNotes from "../Subreddit/UserNotes";
|
||||
import {Logger} from "winston";
|
||||
|
||||
@@ -21,31 +27,47 @@ export interface AuthorActivitiesOptions {
|
||||
|
||||
export async function getAuthorActivities(user: RedditUser, options: AuthorTypedActivitiesOptions): Promise<Array<Submission | Comment>> {
|
||||
|
||||
const {chunkSize: cs = 100} = options;
|
||||
const {chunkSize: cs = 100, window: optWindow} = options;
|
||||
|
||||
let window: number | Dayjs,
|
||||
let satisfiedCount: number | undefined,
|
||||
satisfiedEndtime: Dayjs | undefined,
|
||||
chunkSize = Math.min(cs, 100);
|
||||
if (typeof options.window !== 'number') {
|
||||
|
||||
let durVal: DurationVal | undefined;
|
||||
let duration: Duration | undefined;
|
||||
|
||||
if(isActivityWindowCriteria(optWindow)) {
|
||||
satisfiedCount = optWindow.count;
|
||||
durVal = optWindow.duration;
|
||||
} else if(typeof optWindow === 'number') {
|
||||
satisfiedCount = optWindow;
|
||||
} else {
|
||||
durVal = optWindow as DurationVal;
|
||||
}
|
||||
|
||||
// if count is less than max limit (100) go ahead and just get that many. may result in faster response time for low numbers
|
||||
if(satisfiedCount !== undefined) {
|
||||
chunkSize = Math.min(chunkSize, satisfiedCount);
|
||||
}
|
||||
|
||||
if(durVal !== undefined) {
|
||||
const endTime = dayjs();
|
||||
let d;
|
||||
if (dayjs.isDuration(options.window)) {
|
||||
d = options.window;
|
||||
} else {
|
||||
if (!dayjs.isDuration(durVal)) {
|
||||
// @ts-ignore
|
||||
d = dayjs.duration(options.window);
|
||||
duration = dayjs.duration(durVal);
|
||||
}
|
||||
if (!dayjs.isDuration(d)) {
|
||||
if (!dayjs.isDuration(duration)) {
|
||||
// TODO print object
|
||||
throw new Error('window given was not a number, a valid ISO8601 duration, a Day.js duration, or well-formed Duration options');
|
||||
}
|
||||
window = endTime.subtract(d.asMilliseconds(), 'milliseconds');
|
||||
} else {
|
||||
window = options.window;
|
||||
// use whichever is smaller so we only do one api request if window is smaller than default chunk size
|
||||
chunkSize = Math.min(chunkSize, window);
|
||||
satisfiedEndtime = endTime.subtract(duration.asMilliseconds(), 'milliseconds');
|
||||
}
|
||||
|
||||
if(satisfiedCount === undefined && satisfiedEndtime === undefined) {
|
||||
throw new Error('window value was not valid');
|
||||
}
|
||||
|
||||
let items: Array<Submission | Comment> = [];
|
||||
let lastItemDate;
|
||||
//let count = 1;
|
||||
let listing;
|
||||
switch (options.type) {
|
||||
@@ -63,33 +85,38 @@ export async function getAuthorActivities(user: RedditUser, options: AuthorTyped
|
||||
let offset = chunkSize;
|
||||
while (!hitEnd) {
|
||||
|
||||
if (typeof window === 'number') {
|
||||
hitEnd = listing.length >= window;
|
||||
} else {
|
||||
const listSlice = listing.slice(offset - chunkSize);
|
||||
const listSlice = listing.slice(offset - chunkSize)
|
||||
if (satisfiedCount !== undefined && items.length + listSlice.length >= satisfiedCount) {
|
||||
// satisfied count
|
||||
items = items.concat(listSlice).slice(0, satisfiedCount);
|
||||
break;
|
||||
}
|
||||
|
||||
if(satisfiedEndtime !== undefined) {
|
||||
const truncatedItems = listSlice.filter((x) => {
|
||||
const utc = x.created_utc * 1000;
|
||||
const itemDate = dayjs(utc);
|
||||
// @ts-ignore
|
||||
return window.isBefore(itemDate);
|
||||
return satisfiedEndtime.isBefore(itemDate);
|
||||
});
|
||||
|
||||
if (truncatedItems.length !== listSlice.length) {
|
||||
hitEnd = true;
|
||||
// satisfied duration
|
||||
items = items.concat(truncatedItems);
|
||||
break;
|
||||
}
|
||||
items = items.concat(truncatedItems);
|
||||
}
|
||||
if (!hitEnd) {
|
||||
hitEnd = listing.isFinished;
|
||||
}
|
||||
|
||||
// if we got this far neither count or time was satisfied so just add all items from listing and fetch more is possible
|
||||
items = items.concat(listSlice);
|
||||
|
||||
hitEnd = listing.isFinished;
|
||||
|
||||
if (!hitEnd) {
|
||||
offset += chunkSize;
|
||||
listing = await listing.fetchMore({amount: chunkSize});
|
||||
} else if (typeof window === 'number') {
|
||||
items = listing.slice(0, window + 1);
|
||||
}
|
||||
}
|
||||
// TODO truncate items to window size when duration
|
||||
return Promise.resolve(items);
|
||||
}
|
||||
|
||||
|
||||
10
src/util.ts
10
src/util.ts
@@ -11,6 +11,7 @@ import Submission from "snoowrap/dist/objects/Submission";
|
||||
import {Comment} from "snoowrap";
|
||||
import {inflateSync, deflateSync} from "zlib";
|
||||
import pako from "pako";
|
||||
import {ActivityWindowCriteria} from "./Common/interfaces";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(dduration);
|
||||
@@ -331,3 +332,12 @@ export const deflateUserNotes = (usersObject: object) => {
|
||||
const blob = Buffer.from(binaryData).toString('base64');
|
||||
return blob;
|
||||
}
|
||||
|
||||
export const isActivityWindowCriteria = (val: any): val is ActivityWindowCriteria => {
|
||||
if (val !== null && typeof val === 'object') {
|
||||
return (val.count !== undefined && typeof val.count === 'number') ||
|
||||
// close enough
|
||||
val.duration !== undefined;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user