mirror of
https://github.com/FoxxMD/context-mod.git
synced 2026-01-14 07:57:57 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
161251a943 | ||
|
|
6e4b1b68e3 | ||
|
|
a6212897b3 | ||
|
|
7b8a89e918 | ||
|
|
efd31c5f21 | ||
|
|
868bac9f1a | ||
|
|
adf18cc7ee | ||
|
|
3f1d1bc6d0 |
2
.github/workflows/dockerhub.yml
vendored
2
.github/workflows/dockerhub.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
- 'master'
|
||||
- 'edge'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '*.*.*'
|
||||
# don't trigger if just updating docs
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -43,6 +43,10 @@ a {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
.newRow {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.has-tooltip {
|
||||
/*position: relative;*/
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
33
src/util.ts
33
src/util.ts
@@ -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}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user