Compare commits

..

8 Commits
0.6.0 ... 0.6.1

Author SHA1 Message Date
FoxxMD
161251a943 Merge branch 'edge' 2021-08-05 14:40:06 -04:00
FoxxMD
6e4b1b68e3 Update tag trigger pattern 2021-08-05 13:53:30 -04:00
FoxxMD
a6212897b3 Increase cache hit sample size 2021-08-04 14:44:07 -04:00
FoxxMD
7b8a89e918 Add cache stat averages for key hits 2021-08-04 14:40:47 -04:00
FoxxMD
efd31c5f21 Improve log streaming 2021-08-04 12:45:22 -04:00
FoxxMD
868bac9f1a Fix dry run parsing for arguments 2021-08-04 12:44:51 -04:00
FoxxMD
adf18cc7ee Better check for default cache key count
Look for the first manager using the default rather than assuming the first one configured will have it
2021-08-04 09:56:09 -04:00
FoxxMD
3f1d1bc6d0 Rewrite age regex for schema to not use named capture groups (just regular capture groups) 2021-08-04 09:55:32 -04:00
14 changed files with 200 additions and 56 deletions

View File

@@ -6,7 +6,7 @@ on:
- 'master'
- 'edge'
tags:
- 'v*.*.*'
- '*.*.*'
# don't trigger if just updating docs
paths-ignore:
- '**.md'

View File

@@ -65,7 +65,7 @@ export interface AuthorCriteria {
*
* [See] https://regexr.com/609n8 for example
*
* @pattern ^\s*(?<opStr>>|>=|<|<=)\s*(?<time>\d+)\s*(?<unit>days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\s*$
* @pattern ^\s*(>|>=|<|<=)\s*(\d+)\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\s*$
* */
age?: DurationComparor

View File

@@ -1,4 +1,5 @@
import {Duration} from "dayjs/plugin/duration";
import {Cache} from 'cache-manager';
import Poll from "snoostorm/out/util/Poll";
import Snoowrap from "snoowrap";
@@ -1250,8 +1251,13 @@ export interface OperatorConfig extends OperatorJsonConfig {
interface CacheTypeStat {
requests: number,
miss: number,
missPercent?: string,
identifierRequestCount: Cache
identifierAverageHit: number
requestTimestamps: number[]
averageTimeBetweenHits: string
}
export interface ResourceStats {
[key: string]: CacheTypeStat
[key: string]: CacheTypeStat;
}

View File

@@ -21,7 +21,7 @@
"properties": {
"age": {
"description": "Test the age of the Author's account (when it was created) against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author's account is older than 100 days\n* EX `<= 2 months` => Passes if Author's account is younger than or equal to 2 months\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<time>\\d+)\\s*(?<unit>days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"commentKarma": {

View File

@@ -384,7 +384,7 @@
"properties": {
"age": {
"description": "Test the age of the Author's account (when it was created) against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author's account is older than 100 days\n* EX `<= 2 months` => Passes if Author's account is younger than or equal to 2 months\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<time>\\d+)\\s*(?<unit>days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"commentKarma": {

View File

@@ -330,7 +330,7 @@
"properties": {
"age": {
"description": "Test the age of the Author's account (when it was created) against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author's account is older than 100 days\n* EX `<= 2 months` => Passes if Author's account is younger than or equal to 2 months\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<time>\\d+)\\s*(?<unit>days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"commentKarma": {

View File

@@ -307,7 +307,7 @@
"properties": {
"age": {
"description": "Test the age of the Author's account (when it was created) against this comparison\n\nThe syntax is `(< OR > OR <= OR >=) <number> <unit>`\n\n* EX `> 100 days` => Passes if Author's account is older than 100 days\n* EX `<= 2 months` => Passes if Author's account is younger than or equal to 2 months\n\nUnit must be one of [DayJS Duration units](https://day.js.org/docs/en/durations/creating)\n\n[See] https://regexr.com/609n8 for example",
"pattern": "^\\s*(?<opStr>>|>=|<|<=)\\s*(?<time>\\d+)\\s*(?<unit>days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"pattern": "^\\s*(>|>=|<|<=)\\s*(\\d+)\\s*(days?|weeks?|months?|years?|hours?|minutes?|seconds?|milliseconds?)\\s*$",
"type": "string"
},
"commentKarma": {

View File

@@ -43,6 +43,10 @@ a {
content: ":";
}
.newRow {
margin-top: 15px;
}
.has-tooltip {
/*position: relative;*/
}

View File

@@ -101,7 +101,8 @@ const rcbServer = function (options: OperatorConfig): ([() => Promise<void>, App
let botSubreddits: string[] = [];
stream._write = (chunk, encoding, next) => {
let logLine = chunk.toString();
// remove newline (\n) from end of string since we deal with it with css/html
const logLine = chunk.toString().slice(0, -1);
const now = Date.now();
const logEntry: LogEntry = [now, logLine];
@@ -432,6 +433,8 @@ const rcbServer = function (options: OperatorConfig): ([() => Promise<void>, App
Object.keys(curr.stats.cache.types as ResourceStats).forEach((k) => {
acc[k].requests += curr.stats.cache.types[k].requests;
acc[k].miss += curr.stats.cache.types[k].miss;
acc[k].identifierAverageHit += Number.parseFloat(curr.stats.cache.types[k].identifierAverageHit);
acc[k].averageTimeBetweenHits += curr.stats.cache.types[k].averageTimeBetweenHits === 'N/A' ? 0 : Number.parseFloat(curr.stats.cache.types[k].averageTimeBetweenHits)
});
return acc;
}, cacheStats());
@@ -439,10 +442,13 @@ const rcbServer = function (options: OperatorConfig): ([() => Promise<void>, App
const per = acc[curr].miss === 0 ? 0 : formatNumber(acc[curr].miss / acc[curr].requests) * 100;
// @ts-ignore
acc[curr].missPercent = `${formatNumber(per, {toFixed: 0})}%`;
acc[curr].identifierAverageHit = formatNumber(acc[curr].identifierAverageHit);
acc[curr].averageTimeBetweenHits = formatNumber(acc[curr].averageTimeBetweenHits)
return acc;
}, cumRaw);
const cacheReq = subManagerData.reduce((acc, curr) => acc + curr.stats.cache.totalRequests, 0);
const cacheMiss = subManagerData.reduce((acc, curr) => acc + curr.stats.cache.totalMiss, 0);
const aManagerWithDefaultResources = bot.subManagers.find(x => x.resources !== undefined && x.resources.cacheSettingsHash === 'default');
let allManagerData: any = {
name: 'All',
linkName: 'All',
@@ -464,7 +470,7 @@ const rcbServer = function (options: OperatorConfig): ([() => Promise<void>, App
stats: {
...rest,
cache: {
currentKeyCount: await bot.subManagers[0].resources.getCacheKeyCount(),
currentKeyCount: aManagerWithDefaultResources !== undefined ? await aManagerWithDefaultResources.resources.getCacheKeyCount() : 'N/A',
isShared: false,
totalRequests: cacheReq,
totalMiss: cacheMiss,

View File

@@ -475,40 +475,72 @@
<span>
- Results from <span class="font-mono">window</span> criteria.
</span>
<label>Author Criteria</label>
<span><%= data.stats.cache.types.authorCrit.requests %> | <%= data.stats.cache.types.authorCrit.miss %> (<%= data.stats.cache.types.authorCrit.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.author.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.author.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Author Criteria</label>
<span class="newRow"><%= data.stats.cache.types.authorCrit.requests %> | <%= data.stats.cache.types.authorCrit.miss %> (<%= data.stats.cache.types.authorCrit.missPercent %>) miss</span>
<span class="newRow">
- <span class="font-mono">authorIs</span> results
</span>
<label>Item Criteria</label>
<span><%= data.stats.cache.types.itemCrit.requests %> | <%= data.stats.cache.types.itemCrit.miss %> (<%= data.stats.cache.types.itemCrit.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.authorCrit.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.authorCrit.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Item Criteria</label>
<span class="newRow"><%= data.stats.cache.types.itemCrit.requests %> | <%= data.stats.cache.types.itemCrit.miss %> (<%= data.stats.cache.types.itemCrit.missPercent %>) miss</span>
<span class="newRow">
- <span class="font-mono">itemIs</span> results
</span>
<label>Comment Check</label>
<span><%= data.stats.cache.types.commentCheck.requests %> | <%= data.stats.cache.types.commentCheck.miss %> (<%= data.stats.cache.types.commentCheck.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.itemCrit.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.itemCrit.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Comment Check</label>
<span class="newRow"><%= data.stats.cache.types.commentCheck.requests %> | <%= data.stats.cache.types.commentCheck.miss %> (<%= data.stats.cache.types.commentCheck.missPercent %>) miss</span>
<span class="newRow">
- <span class="font-mono">cacheUserResult</span> results
</span>
<label>Submissions</label>
<span><%= data.stats.cache.types.submission.requests %> | <%= data.stats.cache.types.submission.miss %> (<%= data.stats.cache.types.submission.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.commentCheck.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.commentCheck.averageTimeBetweenHits %>s</span>
<span>
</span>
<label>Comments</label>
<span><%= data.stats.cache.types.comment.requests %> | <%= data.stats.cache.types.comment.miss %> (<%= data.stats.cache.types.comment.missPercent %>) miss</span>
<span>
</span>
<label>Content</label>
<span><%= data.stats.cache.types.content.requests %> | <%= data.stats.cache.types.content.miss %> (<%= data.stats.cache.types.content.missPercent %>) miss</span>
<label class="newRow">Submissions</label>
<span class="newRow"><%= data.stats.cache.types.submission.requests %> | <%= data.stats.cache.types.submission.miss %> (<%= data.stats.cache.types.submission.missPercent %>) miss</span>
<span class="newRow"></span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.submission.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.submission.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Comments</label>
<span class="newRow"><%= data.stats.cache.types.comment.requests %> | <%= data.stats.cache.types.comment.miss %> (<%= data.stats.cache.types.comment.missPercent %>) miss</span>
<span class="newRow"></span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.comment.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.comment.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">Content</label>
<span class="newRow"><%= data.stats.cache.types.content.requests %> | <%= data.stats.cache.types.content.miss %> (<%= data.stats.cache.types.content.missPercent %>) miss</span>
<span class="newRow">
- footer/comment/ban/message content.
</span>
<label>UserNote</label>
<span><%= data.stats.cache.types.userNotes.requests %> | <%= data.stats.cache.types.userNotes.miss %> (<%= data.stats.cache.types.userNotes.missPercent %>) miss</span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.content.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.content.averageTimeBetweenHits %>s</span>
<span>
</span>
<label class="newRow">UserNote</label>
<span class="newRow"><%= data.stats.cache.types.userNotes.requests %> | <%= data.stats.cache.types.userNotes.miss %> (<%= data.stats.cache.types.userNotes.missPercent %>) miss</span>
<span class="newRow"></span>
<label>Avgs</label>
<span>Hits/Key <%= data.stats.cache.types.userNotes.identifierAverageHit %> | Hit Interval <%= data.stats.cache.types.userNotes.averageTimeBetweenHits %>s</span>
<span>
</span>
</div>
</span>
@@ -776,28 +808,62 @@
reconnectionAttempts: 5, // bail after 5 attempts
});
const newBufferedLogs = () => new Map([["All", []]])
let bufferedLogs = newBufferedLogs();
let lastFlush;
let bufferTimeout;
socket.on("connect", () => {
document.body.classList.add('connected')
socket.on("log", data => {
const selectors = ['[data-subreddit="All"].logs'];
bufferedLogs.set('All', bufferedLogs.get('All').concat(data));
const sub = parseSubredditLogName(data);
if (sub !== undefined) {
selectors.push(`[data-subreddit="${sub}"].logs`);
bufferedLogs.set(sub, (bufferedLogs.get(sub) || []).concat(data));
}
const flushLogs = () => {
bufferedLogs.forEach((logs, subKey) => {
const limit = Number.parseInt(document.querySelector(`[data-subreddit="${subKey}"] [data-type="limit"]`).value);
const logContainer = document.querySelector(`[data-subreddit="${subKey}"].logs`);
let existingLogs;
if(window.sort === 'desc') {
logs.forEach((l) => {
logContainer.insertAdjacentHTML('afterbegin', l);
})
existingLogs = Array.from(document.querySelectorAll(`[data-subreddit="${subKey}"].logs .logLine`));
logContainer.replaceChildren(...existingLogs.slice(0, limit));
} else {
logs.forEach((l) => {
logContainer.insertAdjacentHTML('beforeend', l);
existingLogs = Array.from(document.querySelectorAll(`[data-subreddit="${subKey}"].logs .logLine`));
const overLimit = limit - existingLogs.length;
logContainer.replaceChildren(...existingLogs.slice(overLimit -1, limit));
})
}
});
lastFlush = Date.now();
bufferedLogs = newBufferedLogs();
//console.log('Flushed Logs');
}
if(lastFlush !== undefined && bufferTimeout !== undefined && ((Date.now() - lastFlush)/1000) > 3) {
//console.log('Immediate flush');
clearTimeout(bufferTimeout);
bufferTimeout = undefined;
flushLogs();
} else {
//console.log('Using timeout');
clearTimeout(bufferTimeout);
bufferTimeout = setTimeout(() => {flushLogs();}, 1000);
}
selectors.forEach(sel => {
const el = document.querySelector(sel);
if (el !== null) {
const currLogs = el.innerHTML;
document.querySelector(sel).innerHTML = window.sort === 'desc' ? data.concat(currLogs) : currLogs.concat(data)
}
});
});
socket.on("logClear", data => {
data.forEach((obj) => {
const n = obj.name === 'all' ? 'All' : obj.name;
document.querySelector(`[data-subreddit="${n}"].logs`).innerHTML = obj.logs;
})
//document.querySelector('.logs').innerHTML = data.join().replaceAll(/<br\s*\/?>\,/g,'<br />');
});
socket.on('opStats', (data) => {
for (const [k, v] of Object.entries(data)) {

View File

@@ -166,7 +166,7 @@ export class Manager {
};
if (this.resources !== undefined) {
const resStats = this.resources.getStats();
const resStats = await this.resources.getStats();
data.cache = resStats.cache;
data.cache.currentKeyCount = await this.resources.getCacheKeyCount();

View File

@@ -109,6 +109,7 @@ export class SubredditResources {
};
const cacheUseCB = (miss: boolean) => {
this.stats.cache.userNotes.requestTimestamps.push(Date.now());
this.stats.cache.userNotes.requests++;
this.stats.cache.userNotes.miss += miss ? 1 : 0;
}
@@ -135,23 +136,56 @@ export class SubredditResources {
return 0;
}
getStats() {
async getStats() {
const totals = Object.values(this.stats.cache).reduce((acc, curr) => ({
miss: acc.miss + curr.miss,
req: acc.req + curr.requests,
}), {miss: 0, req: 0});
const cacheKeys = Object.keys(this.stats.cache);
return {
cache: {
// TODO could probably combine these two
totalRequests: totals.req,
totalMiss: totals.miss,
missPercent: `${formatNumber(totals.miss === 0 || totals.req === 0 ? 0 :(totals.miss/totals.req) * 100, {toFixed: 0})}%`,
types: Object.keys(this.stats.cache).reduce((acc, curr) => {
types: await cacheKeys.reduce(async (accProm, curr) => {
const acc = await accProm;
// calculate miss percent
const per = acc[curr].miss === 0 ? 0 : formatNumber(acc[curr].miss / acc[curr].requests) * 100;
// @ts-ignore
acc[curr].missPercent = `${formatNumber(per, {toFixed: 0})}%`;
// calculate average identifier hits
const idCache = acc[curr].identifierRequestCount;
// @ts-expect-error
const idKeys = await idCache.store.keys() as string[];
if(idKeys.length > 0) {
let hits = 0;
for (const k of idKeys) {
hits += await idCache.get(k) as number;
}
acc[curr].identifierAverageHit = formatNumber(hits/idKeys.length);
}
if(acc[curr].requestTimestamps.length > 1) {
// calculate average time between request
const diffData = acc[curr].requestTimestamps.reduce((acc, curr: number) => {
if(acc.last === 0) {
acc.last = curr;
return acc;
}
acc.diffs.push(curr - acc.last);
acc.last = curr;
return acc;
},{last: 0, diffs: [] as number[]});
const avgDiff = diffData.diffs.reduce((acc, curr) => acc + curr, 0) / diffData.diffs.length;
acc[curr].averageTimeBetweenHits = formatNumber(avgDiff/1000);
}
return acc;
}, this.stats.cache)
}, Promise.resolve(this.stats.cache))
}
}
}
@@ -162,9 +196,13 @@ export class SubredditResources {
async getActivity(item: Submission | Comment) {
try {
let hash = '';
if (item instanceof Submission && this.submissionTTL > 0) {
hash = `sub-${item.name}`;
await this.stats.cache.submission.identifierRequestCount.set(hash, (await this.stats.cache.submission.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.submission.requestTimestamps.push(Date.now());
this.stats.cache.submission.requests++;
const cachedSubmission = await this.cache.get(`sub-${item.name}`);
const cachedSubmission = await this.cache.get(hash);
if (cachedSubmission !== undefined) {
this.logger.debug(`Cache Hit: Submission ${item.name}`);
return cachedSubmission;
@@ -172,11 +210,14 @@ export class SubredditResources {
// @ts-ignore
const submission = await item.fetch();
this.stats.cache.submission.miss++;
await this.cache.set(`sub-${item.name}`, submission, {ttl: this.submissionTTL});
await this.cache.set(hash, submission, {ttl: this.submissionTTL});
return submission;
} else if (this.commentTTL > 0) {
hash = `comm-${item.name}`;
await this.stats.cache.comment.identifierRequestCount.set(hash, (await this.stats.cache.comment.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.comment.requestTimestamps.push(Date.now());
this.stats.cache.comment.requests++;
const cachedComment = await this.cache.get(`comm-${item.name}`);
const cachedComment = await this.cache.get(hash);
if (cachedComment !== undefined) {
this.logger.debug(`Cache Hit: Comment ${item.name}`);
return cachedComment;
@@ -184,7 +225,7 @@ export class SubredditResources {
// @ts-ignore
const comment = await item.fetch();
this.stats.cache.comment.miss++;
await this.cache.set(`comm-${item.name}`, comment, {ttl: this.commentTTL});
await this.cache.set(hash, comment, {ttl: this.commentTTL});
return comment;
} else {
// @ts-ignore
@@ -206,6 +247,8 @@ export class SubredditResources {
const hash = objectHash.sha1({...options, userName});
this.stats.cache.author.requests++;
await this.stats.cache.author.identifierRequestCount.set(user.name, (await this.stats.cache.author.identifierRequestCount.wrap(user.name, () => 0) as number) + 1);
this.stats.cache.author.requestTimestamps.push(Date.now());
let miss = false;
const cacheVal = await this.cache.wrap(hash, async () => {
miss = true;
@@ -251,6 +294,8 @@ export class SubredditResources {
// try to get cached value first
let hash = `${subreddit.display_name}-${cacheKey}`;
if (this.wikiTTL > 0) {
await this.stats.cache.content.identifierRequestCount.set(cacheKey, (await this.stats.cache.content.identifierRequestCount.wrap(cacheKey, () => 0) as number) + 1);
this.stats.cache.content.requestTimestamps.push(Date.now());
this.stats.cache.content.requests++;
const cachedContent = await this.cache.get(hash);
if (cachedContent !== undefined) {
@@ -311,6 +356,8 @@ export class SubredditResources {
if (this.filterCriteriaTTL > 0) {
const hashObj = {itemId: item.id, ...authorOpts, include};
const hash = `authorCrit-${objectHash.sha1(hashObj)}`;
await this.stats.cache.authorCrit.identifierRequestCount.set(hash, (await this.stats.cache.authorCrit.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.authorCrit.requestTimestamps.push(Date.now());
this.stats.cache.authorCrit.requests++;
let miss = false;
const cachedAuthorTest = await this.cache.wrap(hash, async () => {
@@ -345,6 +392,8 @@ export class SubredditResources {
try {
const hashObj = {itemId: item.name, ...states};
const hash = `itemCrit-${objectHash.sha1(hashObj)}`;
await this.stats.cache.itemCrit.identifierRequestCount.set(hash, (await this.stats.cache.itemCrit.identifierRequestCount.wrap(hash, () => 0) as number) + 1);
this.stats.cache.itemCrit.requestTimestamps.push(Date.now());
this.stats.cache.itemCrit.requests++;
const cachedItem = await this.cache.get(hash);
if (cachedItem !== undefined) {

View File

@@ -40,7 +40,7 @@ export const hardLimit = new commander.Option('--hardLimit <limit>', 'When API l
.argParser(argParseInt);
export const dryRun = new commander.Option('--dryRun', 'Set all subreddits in dry run mode, overriding configurations (default: process.env.DRYRUN || false)')
.argParser(parseBoolWithDefault(undefined));
.default(undefined);
export const checks = new commander.Option('-h, --checks <checkNames...>', 'An optional list of Checks, by name, that should be run. If none are specified all Checks for the Subreddit the Activity is in will be run');

View File

@@ -362,7 +362,9 @@ export function parseBool(value: any, prev: any = false): boolean {
throw new InvalidOptionArgumentError('Not a boolean value.');
}
export const parseBoolWithDefault = (defaultValue: any) => (arg: any) => parseBool(arg, defaultValue);
export const parseBoolWithDefault = (defaultValue: any) => (arg: any, prevVal: any) => {
parseBool(arg, defaultValue)
};
export function activityWindowText(activities: (Submission | Comment)[], suffix = false): (string | undefined) {
if (activities.length === 0) {
@@ -670,7 +672,7 @@ export const isLogLineMinLevel = (line: string, minLevelText: string): boolean =
// https://regexr.com/3e6m0
const HYPERLINK_REGEX: RegExp = /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;
export const formatLogLineToHtml = (val: string) => {
return val
const logContent = val
.replace(/(\s*debug\s*):/i, '<span class="debug text-pink-400">$1</span>:')
.replace(/(\s*warn\s*):/i, '<span class="warn text-yellow-400">$1</span>:')
.replace(/(\s*info\s*):/i, '<span class="info text-blue-300">$1</span>:')
@@ -678,6 +680,7 @@ export const formatLogLineToHtml = (val: string) => {
.replace(/(\s*verbose\s*):/i, '<span class="error text-purple-400">$1</span>:')
.replaceAll('\n', '<br />')
.replace(HYPERLINK_REGEX, '<a target="_blank" href="$&">$&</a>');
return `<div class="logLine">${logContent}</div>`
}
export type LogEntry = [number, string];
@@ -873,16 +876,26 @@ export const removeUndefinedKeys = (obj: any) => {
return newObj;
}
const timestampArr = () => {
const arr: number[] = [];
arr.length = 50;
return arr;
}
const statMetricCache = () => {
return cacheManager.caching({store: 'memory', max: 50, ttl: 0});
}
export const cacheStats = (): ResourceStats => {
return {
author: {requests: 0, miss: 0},
authorCrit: {requests: 0, miss: 0},
itemCrit: {requests: 0, miss: 0},
content: {requests: 0, miss: 0},
userNotes: {requests: 0, miss: 0},
submission: {requests: 0, miss: 0},
comment: {requests: 0, miss: 0},
commentCheck: {requests: 0, miss: 0}
author: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
authorCrit: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
itemCrit: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
content: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
userNotes: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
submission: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
comment: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0},
commentCheck: {requests: 0, miss: 0, identifierRequestCount: statMetricCache(), requestTimestamps: timestampArr(), averageTimeBetweenHits: 'N/A', identifierAverageHit: 0}
};
}