@wise-old-man/utils 3.3.11 → 3.3.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +229 -0
- package/dist/es/index.js +229 -0
- package/dist/es/index.mjs +229 -0
- package/dist/index.d.ts +3 -4
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var axios = require('axios');
|
|
4
|
+
var prometheus = require('prom-client');
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
require('dotenv/config');
|
|
7
|
+
var fetchable = require('@attio/fetchable');
|
|
8
|
+
|
|
3
9
|
var config = {
|
|
4
10
|
defaultUserAgent: `WiseOldMan JS Client v${process.env.npm_package_version}`,
|
|
5
11
|
baseAPIUrl: 'https://api.wiseoldman.net/v2'
|
|
@@ -644,6 +650,8 @@ const PlayerType = {
|
|
|
644
650
|
};
|
|
645
651
|
const PlayerAnnotationType = {
|
|
646
652
|
OPT_OUT: 'opt_out',
|
|
653
|
+
OPT_OUT_GROUPS: 'opt_out_groups',
|
|
654
|
+
OPT_OUT_COMPETITIONS: 'opt_out_competitions',
|
|
647
655
|
BLOCKED: 'blocked',
|
|
648
656
|
FAKE_F2P: 'fake_f2p'
|
|
649
657
|
};
|
|
@@ -2059,6 +2067,226 @@ function getParentEfficiencyMetric(metric) {
|
|
|
2059
2067
|
return null;
|
|
2060
2068
|
}
|
|
2061
2069
|
|
|
2070
|
+
/**
|
|
2071
|
+
* This file has been created as a way to force any usage
|
|
2072
|
+
* of process.env to go through a dotenv.config first.
|
|
2073
|
+
*/
|
|
2074
|
+
/**
|
|
2075
|
+
* This ensures that an env var is required in prod but optional in dev/test.
|
|
2076
|
+
*/
|
|
2077
|
+
function prodOnly(varSchema) {
|
|
2078
|
+
if (process.env.NODE_ENV === 'production') {
|
|
2079
|
+
return varSchema;
|
|
2080
|
+
}
|
|
2081
|
+
return zod.z.optional(varSchema);
|
|
2082
|
+
}
|
|
2083
|
+
const envVariablesSchema = zod.z.object({
|
|
2084
|
+
// Prisma Database URL
|
|
2085
|
+
CORE_DATABASE_URL: zod.z.string().trim().min(1),
|
|
2086
|
+
// Redis Configs
|
|
2087
|
+
REDIS_HOST: zod.z.string().trim().min(1),
|
|
2088
|
+
REDIS_PORT: zod.z.coerce.number().positive().int(),
|
|
2089
|
+
// Node Environment
|
|
2090
|
+
NODE_ENV: zod.z.enum(['development', 'production', 'test']),
|
|
2091
|
+
// Port for the API to run on
|
|
2092
|
+
API_PORT: zod.z.optional(zod.z.coerce.number().positive().int()),
|
|
2093
|
+
// Admin Password (For mod+ operations)
|
|
2094
|
+
ADMIN_PASSWORD: prodOnly(zod.z.string().trim().min(1)),
|
|
2095
|
+
// Sentry (for error tracking)
|
|
2096
|
+
API_SENTRY_DSN: prodOnly(zod.z.string().trim().min(1)),
|
|
2097
|
+
// Patreon Token (to access their API)
|
|
2098
|
+
PATREON_BEARER_TOKEN: prodOnly(zod.z.string().trim().min(1)),
|
|
2099
|
+
// Discord Bot API URL (to send events to)
|
|
2100
|
+
DISCORD_BOT_API_URL: prodOnly(zod.z.string().trim().min(1).url()),
|
|
2101
|
+
// Our Prometheus metrics aggregator service URL
|
|
2102
|
+
PROMETHEUS_METRICS_SERVICE_URL: prodOnly(zod.z.string().trim().min(1).url()),
|
|
2103
|
+
// Discord Monitoring Webhooks
|
|
2104
|
+
DISCORD_PATREON_WEBHOOK_URL: prodOnly(zod.z.string().trim().min(1).url()),
|
|
2105
|
+
DISCORD_MONITORING_WEBHOOK_URL: prodOnly(zod.z.string().trim().min(1).url()),
|
|
2106
|
+
// Proxy Configs
|
|
2107
|
+
PROXY_LIST: prodOnly(zod.z.string().trim().min(1)),
|
|
2108
|
+
PROXY_USER: prodOnly(zod.z.string().trim().min(1)),
|
|
2109
|
+
PROXY_PASSWORD: prodOnly(zod.z.string().trim().min(1)),
|
|
2110
|
+
PROXY_PORT: prodOnly(zod.z.coerce.number().positive().int()),
|
|
2111
|
+
CPU_COUNT: prodOnly(zod.z.coerce.number().positive().int()),
|
|
2112
|
+
// Openai API Key
|
|
2113
|
+
OPENAI_API_KEY: prodOnly(zod.z.string().trim().min(1).startsWith('sk-'))
|
|
2114
|
+
});
|
|
2115
|
+
// This will load env vars from a .env file, type check them,and throw an error
|
|
2116
|
+
// (interrupting the process) if they're required and missing, or of an invalid type.
|
|
2117
|
+
try {
|
|
2118
|
+
envVariablesSchema.parse(process.env);
|
|
2119
|
+
}
|
|
2120
|
+
catch (error) {
|
|
2121
|
+
const errorPayload = JSON.stringify(error, null, 2);
|
|
2122
|
+
throw new Error(`Invalid environment variables. Please check env.ts for more info.\n${errorPayload}`);
|
|
2123
|
+
}
|
|
2124
|
+
function getThreadIndex() {
|
|
2125
|
+
if (process.env.pm_id === undefined) {
|
|
2126
|
+
return null;
|
|
2127
|
+
}
|
|
2128
|
+
return parseInt(process.env.pm_id, 10);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
class PrometheusService {
|
|
2132
|
+
constructor() {
|
|
2133
|
+
this.pushInterval = null;
|
|
2134
|
+
this.registry = new prometheus.Registry();
|
|
2135
|
+
this.registry.setDefaultLabels({ app: 'wise-old-man', threadIndex: getThreadIndex() });
|
|
2136
|
+
prometheus.collectDefaultMetrics({ register: this.registry });
|
|
2137
|
+
this.effectHistogram = new prometheus.Histogram({
|
|
2138
|
+
name: 'effect_duration_seconds',
|
|
2139
|
+
help: 'Duration of effects in microseconds',
|
|
2140
|
+
labelNames: ['effectName', 'status'],
|
|
2141
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
|
|
2142
|
+
});
|
|
2143
|
+
this.httpHistogram = new prometheus.Histogram({
|
|
2144
|
+
name: 'http_request_duration_seconds',
|
|
2145
|
+
help: 'Duration of HTTP requests in microseconds',
|
|
2146
|
+
labelNames: ['method', 'route', 'status', 'userAgent'],
|
|
2147
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
|
|
2148
|
+
});
|
|
2149
|
+
this.jobHistogram = new prometheus.Histogram({
|
|
2150
|
+
name: 'job_duration_seconds',
|
|
2151
|
+
help: 'Duration of jobs in microseconds',
|
|
2152
|
+
labelNames: ['jobName', 'status'],
|
|
2153
|
+
buckets: [0.1, 0.5, 1, 5, 10, 30, 60]
|
|
2154
|
+
});
|
|
2155
|
+
this.jobQueueGauge = new prometheus.Gauge({
|
|
2156
|
+
name: 'job_queue_size',
|
|
2157
|
+
help: 'Number of jobs in different states for each queue',
|
|
2158
|
+
labelNames: ['queueName', 'state']
|
|
2159
|
+
});
|
|
2160
|
+
this.eventCounter = new prometheus.Counter({
|
|
2161
|
+
name: 'event_counter',
|
|
2162
|
+
help: 'Count of events emitted',
|
|
2163
|
+
labelNames: ['eventType']
|
|
2164
|
+
});
|
|
2165
|
+
this.customPeriodCounter = new prometheus.Counter({
|
|
2166
|
+
name: 'custom_period_counter',
|
|
2167
|
+
help: 'Count of custom period expressions used',
|
|
2168
|
+
labelNames: ['customPeriod']
|
|
2169
|
+
});
|
|
2170
|
+
this.updatePlayerJobSourceCounter = new prometheus.Counter({
|
|
2171
|
+
name: 'update_player_job_source_counter',
|
|
2172
|
+
help: 'Count of update player jobs dispatched',
|
|
2173
|
+
labelNames: ['source']
|
|
2174
|
+
});
|
|
2175
|
+
this.runeMetricsHistogram = new prometheus.Histogram({
|
|
2176
|
+
name: 'runemetrics_duration_seconds',
|
|
2177
|
+
help: 'Duration of RuneMetrics requests in microseconds',
|
|
2178
|
+
labelNames: ['status'],
|
|
2179
|
+
buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
|
|
2180
|
+
});
|
|
2181
|
+
this.hiscoresHistogram = new prometheus.Histogram({
|
|
2182
|
+
name: 'hiscores_duration_seconds',
|
|
2183
|
+
help: 'Duration of hiscores requests in microseconds',
|
|
2184
|
+
labelNames: ['status'],
|
|
2185
|
+
buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
|
|
2186
|
+
});
|
|
2187
|
+
this.registry.registerMetric(this.jobHistogram);
|
|
2188
|
+
this.registry.registerMetric(this.jobQueueGauge);
|
|
2189
|
+
this.registry.registerMetric(this.httpHistogram);
|
|
2190
|
+
this.registry.registerMetric(this.effectHistogram);
|
|
2191
|
+
this.registry.registerMetric(this.eventCounter);
|
|
2192
|
+
this.registry.registerMetric(this.customPeriodCounter);
|
|
2193
|
+
this.registry.registerMetric(this.updatePlayerJobSourceCounter);
|
|
2194
|
+
this.registry.registerMetric(this.runeMetricsHistogram);
|
|
2195
|
+
this.registry.registerMetric(this.hiscoresHistogram);
|
|
2196
|
+
}
|
|
2197
|
+
init() {
|
|
2198
|
+
this.pushInterval = setInterval(() => {
|
|
2199
|
+
this.pushMetrics();
|
|
2200
|
+
}, 60000);
|
|
2201
|
+
}
|
|
2202
|
+
shutdown() {
|
|
2203
|
+
if (this.pushInterval !== null) {
|
|
2204
|
+
clearInterval(this.pushInterval);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
pushMetrics() {
|
|
2208
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2209
|
+
if (process.env.NODE_ENV === 'test') {
|
|
2210
|
+
return fetchable.errored({ code: 'NOT_ALLOWED_IN_TEST_ENV' });
|
|
2211
|
+
}
|
|
2212
|
+
if (!process.env.PROMETHEUS_METRICS_SERVICE_URL) {
|
|
2213
|
+
return fetchable.errored({ code: 'MISSING_METRICS_URL' });
|
|
2214
|
+
}
|
|
2215
|
+
const metricsResult = yield fetchable.fromPromise(this.registry.getMetricsAsJSON());
|
|
2216
|
+
if (fetchable.isErrored(metricsResult)) {
|
|
2217
|
+
return fetchable.errored({
|
|
2218
|
+
code: 'FAILED_TO_GET_PROMETHEUS_METRICS',
|
|
2219
|
+
subError: metricsResult.error
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
const requestResult = yield fetchable.fromPromise(axios.post(process.env.PROMETHEUS_METRICS_SERVICE_URL, {
|
|
2223
|
+
source: 'api',
|
|
2224
|
+
data: metricsResult.value,
|
|
2225
|
+
threadIndex: getThreadIndex()
|
|
2226
|
+
}));
|
|
2227
|
+
if (fetchable.isErrored(requestResult)) {
|
|
2228
|
+
return fetchable.errored({
|
|
2229
|
+
code: 'FAILED_TO_PUSH_PROMETHEUS_METRICS',
|
|
2230
|
+
subError: requestResult.error
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
return fetchable.complete(true);
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
trackHttpRequest() {
|
|
2237
|
+
return this.httpHistogram.startTimer();
|
|
2238
|
+
}
|
|
2239
|
+
trackRuneMetricsRequest() {
|
|
2240
|
+
return this.runeMetricsHistogram.startTimer();
|
|
2241
|
+
}
|
|
2242
|
+
trackHiscoresRequest() {
|
|
2243
|
+
return this.hiscoresHistogram.startTimer();
|
|
2244
|
+
}
|
|
2245
|
+
trackEffect(effectName, fn) {
|
|
2246
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2247
|
+
const endTimer = this.effectHistogram.startTimer();
|
|
2248
|
+
try {
|
|
2249
|
+
yield fn();
|
|
2250
|
+
endTimer({ effectName, status: 1 });
|
|
2251
|
+
}
|
|
2252
|
+
catch (error) {
|
|
2253
|
+
endTimer({ effectName, status: 0 });
|
|
2254
|
+
throw error;
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
trackJob(jobName, handler) {
|
|
2259
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2260
|
+
const endTimer = this.jobHistogram.startTimer();
|
|
2261
|
+
try {
|
|
2262
|
+
yield handler();
|
|
2263
|
+
endTimer({ jobName, status: 1 });
|
|
2264
|
+
}
|
|
2265
|
+
catch (error) {
|
|
2266
|
+
endTimer({ jobName, status: 0 });
|
|
2267
|
+
throw error;
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
trackEventEmitted(eventType) {
|
|
2272
|
+
this.eventCounter.inc({ eventType });
|
|
2273
|
+
}
|
|
2274
|
+
trackCustomPeriodExpression(customPeriod) {
|
|
2275
|
+
this.customPeriodCounter.inc({ customPeriod });
|
|
2276
|
+
}
|
|
2277
|
+
trackUpdatePlayerJobSource(source) {
|
|
2278
|
+
this.updatePlayerJobSourceCounter.inc({ source });
|
|
2279
|
+
}
|
|
2280
|
+
updateQueueMetrics(queueName, counts) {
|
|
2281
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2282
|
+
for (const [state, count] of Object.entries(counts)) {
|
|
2283
|
+
this.jobQueueGauge.set({ queueName, state }, count);
|
|
2284
|
+
}
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
var prometheusService = new PrometheusService();
|
|
2289
|
+
|
|
2062
2290
|
const CUSTOM_PERIOD_REGEX = /(\d+y)?(\d+m)?(\d+w)?(\d+d)?(\d+h)?/;
|
|
2063
2291
|
const PeriodProps = {
|
|
2064
2292
|
[Period.FIVE_MIN]: { name: '5 Min', milliseconds: 300000 },
|
|
@@ -2086,6 +2314,7 @@ function parsePeriodExpression(periodExpression) {
|
|
|
2086
2314
|
durationMs: PeriodProps[fixed].milliseconds
|
|
2087
2315
|
};
|
|
2088
2316
|
}
|
|
2317
|
+
prometheusService.trackCustomPeriodExpression(fixed);
|
|
2089
2318
|
const result = fixed.match(CUSTOM_PERIOD_REGEX);
|
|
2090
2319
|
if (!result || result.length === 0 || result[0] !== fixed)
|
|
2091
2320
|
return null;
|
package/dist/es/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import prometheus from 'prom-client';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import 'dotenv/config';
|
|
5
|
+
import { errored, fromPromise, isErrored, complete } from '@attio/fetchable';
|
|
6
|
+
|
|
1
7
|
var config = {
|
|
2
8
|
defaultUserAgent: `WiseOldMan JS Client v${process.env.npm_package_version}`,
|
|
3
9
|
baseAPIUrl: 'https://api.wiseoldman.net/v2'
|
|
@@ -642,6 +648,8 @@ const PlayerType = {
|
|
|
642
648
|
};
|
|
643
649
|
const PlayerAnnotationType = {
|
|
644
650
|
OPT_OUT: 'opt_out',
|
|
651
|
+
OPT_OUT_GROUPS: 'opt_out_groups',
|
|
652
|
+
OPT_OUT_COMPETITIONS: 'opt_out_competitions',
|
|
645
653
|
BLOCKED: 'blocked',
|
|
646
654
|
FAKE_F2P: 'fake_f2p'
|
|
647
655
|
};
|
|
@@ -2057,6 +2065,226 @@ function getParentEfficiencyMetric(metric) {
|
|
|
2057
2065
|
return null;
|
|
2058
2066
|
}
|
|
2059
2067
|
|
|
2068
|
+
/**
|
|
2069
|
+
* This file has been created as a way to force any usage
|
|
2070
|
+
* of process.env to go through a dotenv.config first.
|
|
2071
|
+
*/
|
|
2072
|
+
/**
|
|
2073
|
+
* This ensures that an env var is required in prod but optional in dev/test.
|
|
2074
|
+
*/
|
|
2075
|
+
function prodOnly(varSchema) {
|
|
2076
|
+
if (process.env.NODE_ENV === 'production') {
|
|
2077
|
+
return varSchema;
|
|
2078
|
+
}
|
|
2079
|
+
return z.optional(varSchema);
|
|
2080
|
+
}
|
|
2081
|
+
const envVariablesSchema = z.object({
|
|
2082
|
+
// Prisma Database URL
|
|
2083
|
+
CORE_DATABASE_URL: z.string().trim().min(1),
|
|
2084
|
+
// Redis Configs
|
|
2085
|
+
REDIS_HOST: z.string().trim().min(1),
|
|
2086
|
+
REDIS_PORT: z.coerce.number().positive().int(),
|
|
2087
|
+
// Node Environment
|
|
2088
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
2089
|
+
// Port for the API to run on
|
|
2090
|
+
API_PORT: z.optional(z.coerce.number().positive().int()),
|
|
2091
|
+
// Admin Password (For mod+ operations)
|
|
2092
|
+
ADMIN_PASSWORD: prodOnly(z.string().trim().min(1)),
|
|
2093
|
+
// Sentry (for error tracking)
|
|
2094
|
+
API_SENTRY_DSN: prodOnly(z.string().trim().min(1)),
|
|
2095
|
+
// Patreon Token (to access their API)
|
|
2096
|
+
PATREON_BEARER_TOKEN: prodOnly(z.string().trim().min(1)),
|
|
2097
|
+
// Discord Bot API URL (to send events to)
|
|
2098
|
+
DISCORD_BOT_API_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2099
|
+
// Our Prometheus metrics aggregator service URL
|
|
2100
|
+
PROMETHEUS_METRICS_SERVICE_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2101
|
+
// Discord Monitoring Webhooks
|
|
2102
|
+
DISCORD_PATREON_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2103
|
+
DISCORD_MONITORING_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2104
|
+
// Proxy Configs
|
|
2105
|
+
PROXY_LIST: prodOnly(z.string().trim().min(1)),
|
|
2106
|
+
PROXY_USER: prodOnly(z.string().trim().min(1)),
|
|
2107
|
+
PROXY_PASSWORD: prodOnly(z.string().trim().min(1)),
|
|
2108
|
+
PROXY_PORT: prodOnly(z.coerce.number().positive().int()),
|
|
2109
|
+
CPU_COUNT: prodOnly(z.coerce.number().positive().int()),
|
|
2110
|
+
// Openai API Key
|
|
2111
|
+
OPENAI_API_KEY: prodOnly(z.string().trim().min(1).startsWith('sk-'))
|
|
2112
|
+
});
|
|
2113
|
+
// This will load env vars from a .env file, type check them,and throw an error
|
|
2114
|
+
// (interrupting the process) if they're required and missing, or of an invalid type.
|
|
2115
|
+
try {
|
|
2116
|
+
envVariablesSchema.parse(process.env);
|
|
2117
|
+
}
|
|
2118
|
+
catch (error) {
|
|
2119
|
+
const errorPayload = JSON.stringify(error, null, 2);
|
|
2120
|
+
throw new Error(`Invalid environment variables. Please check env.ts for more info.\n${errorPayload}`);
|
|
2121
|
+
}
|
|
2122
|
+
function getThreadIndex() {
|
|
2123
|
+
if (process.env.pm_id === undefined) {
|
|
2124
|
+
return null;
|
|
2125
|
+
}
|
|
2126
|
+
return parseInt(process.env.pm_id, 10);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
class PrometheusService {
|
|
2130
|
+
constructor() {
|
|
2131
|
+
this.pushInterval = null;
|
|
2132
|
+
this.registry = new prometheus.Registry();
|
|
2133
|
+
this.registry.setDefaultLabels({ app: 'wise-old-man', threadIndex: getThreadIndex() });
|
|
2134
|
+
prometheus.collectDefaultMetrics({ register: this.registry });
|
|
2135
|
+
this.effectHistogram = new prometheus.Histogram({
|
|
2136
|
+
name: 'effect_duration_seconds',
|
|
2137
|
+
help: 'Duration of effects in microseconds',
|
|
2138
|
+
labelNames: ['effectName', 'status'],
|
|
2139
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
|
|
2140
|
+
});
|
|
2141
|
+
this.httpHistogram = new prometheus.Histogram({
|
|
2142
|
+
name: 'http_request_duration_seconds',
|
|
2143
|
+
help: 'Duration of HTTP requests in microseconds',
|
|
2144
|
+
labelNames: ['method', 'route', 'status', 'userAgent'],
|
|
2145
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
|
|
2146
|
+
});
|
|
2147
|
+
this.jobHistogram = new prometheus.Histogram({
|
|
2148
|
+
name: 'job_duration_seconds',
|
|
2149
|
+
help: 'Duration of jobs in microseconds',
|
|
2150
|
+
labelNames: ['jobName', 'status'],
|
|
2151
|
+
buckets: [0.1, 0.5, 1, 5, 10, 30, 60]
|
|
2152
|
+
});
|
|
2153
|
+
this.jobQueueGauge = new prometheus.Gauge({
|
|
2154
|
+
name: 'job_queue_size',
|
|
2155
|
+
help: 'Number of jobs in different states for each queue',
|
|
2156
|
+
labelNames: ['queueName', 'state']
|
|
2157
|
+
});
|
|
2158
|
+
this.eventCounter = new prometheus.Counter({
|
|
2159
|
+
name: 'event_counter',
|
|
2160
|
+
help: 'Count of events emitted',
|
|
2161
|
+
labelNames: ['eventType']
|
|
2162
|
+
});
|
|
2163
|
+
this.customPeriodCounter = new prometheus.Counter({
|
|
2164
|
+
name: 'custom_period_counter',
|
|
2165
|
+
help: 'Count of custom period expressions used',
|
|
2166
|
+
labelNames: ['customPeriod']
|
|
2167
|
+
});
|
|
2168
|
+
this.updatePlayerJobSourceCounter = new prometheus.Counter({
|
|
2169
|
+
name: 'update_player_job_source_counter',
|
|
2170
|
+
help: 'Count of update player jobs dispatched',
|
|
2171
|
+
labelNames: ['source']
|
|
2172
|
+
});
|
|
2173
|
+
this.runeMetricsHistogram = new prometheus.Histogram({
|
|
2174
|
+
name: 'runemetrics_duration_seconds',
|
|
2175
|
+
help: 'Duration of RuneMetrics requests in microseconds',
|
|
2176
|
+
labelNames: ['status'],
|
|
2177
|
+
buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
|
|
2178
|
+
});
|
|
2179
|
+
this.hiscoresHistogram = new prometheus.Histogram({
|
|
2180
|
+
name: 'hiscores_duration_seconds',
|
|
2181
|
+
help: 'Duration of hiscores requests in microseconds',
|
|
2182
|
+
labelNames: ['status'],
|
|
2183
|
+
buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
|
|
2184
|
+
});
|
|
2185
|
+
this.registry.registerMetric(this.jobHistogram);
|
|
2186
|
+
this.registry.registerMetric(this.jobQueueGauge);
|
|
2187
|
+
this.registry.registerMetric(this.httpHistogram);
|
|
2188
|
+
this.registry.registerMetric(this.effectHistogram);
|
|
2189
|
+
this.registry.registerMetric(this.eventCounter);
|
|
2190
|
+
this.registry.registerMetric(this.customPeriodCounter);
|
|
2191
|
+
this.registry.registerMetric(this.updatePlayerJobSourceCounter);
|
|
2192
|
+
this.registry.registerMetric(this.runeMetricsHistogram);
|
|
2193
|
+
this.registry.registerMetric(this.hiscoresHistogram);
|
|
2194
|
+
}
|
|
2195
|
+
init() {
|
|
2196
|
+
this.pushInterval = setInterval(() => {
|
|
2197
|
+
this.pushMetrics();
|
|
2198
|
+
}, 60000);
|
|
2199
|
+
}
|
|
2200
|
+
shutdown() {
|
|
2201
|
+
if (this.pushInterval !== null) {
|
|
2202
|
+
clearInterval(this.pushInterval);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
pushMetrics() {
|
|
2206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2207
|
+
if (process.env.NODE_ENV === 'test') {
|
|
2208
|
+
return errored({ code: 'NOT_ALLOWED_IN_TEST_ENV' });
|
|
2209
|
+
}
|
|
2210
|
+
if (!process.env.PROMETHEUS_METRICS_SERVICE_URL) {
|
|
2211
|
+
return errored({ code: 'MISSING_METRICS_URL' });
|
|
2212
|
+
}
|
|
2213
|
+
const metricsResult = yield fromPromise(this.registry.getMetricsAsJSON());
|
|
2214
|
+
if (isErrored(metricsResult)) {
|
|
2215
|
+
return errored({
|
|
2216
|
+
code: 'FAILED_TO_GET_PROMETHEUS_METRICS',
|
|
2217
|
+
subError: metricsResult.error
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
const requestResult = yield fromPromise(axios.post(process.env.PROMETHEUS_METRICS_SERVICE_URL, {
|
|
2221
|
+
source: 'api',
|
|
2222
|
+
data: metricsResult.value,
|
|
2223
|
+
threadIndex: getThreadIndex()
|
|
2224
|
+
}));
|
|
2225
|
+
if (isErrored(requestResult)) {
|
|
2226
|
+
return errored({
|
|
2227
|
+
code: 'FAILED_TO_PUSH_PROMETHEUS_METRICS',
|
|
2228
|
+
subError: requestResult.error
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
return complete(true);
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
2234
|
+
trackHttpRequest() {
|
|
2235
|
+
return this.httpHistogram.startTimer();
|
|
2236
|
+
}
|
|
2237
|
+
trackRuneMetricsRequest() {
|
|
2238
|
+
return this.runeMetricsHistogram.startTimer();
|
|
2239
|
+
}
|
|
2240
|
+
trackHiscoresRequest() {
|
|
2241
|
+
return this.hiscoresHistogram.startTimer();
|
|
2242
|
+
}
|
|
2243
|
+
trackEffect(effectName, fn) {
|
|
2244
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2245
|
+
const endTimer = this.effectHistogram.startTimer();
|
|
2246
|
+
try {
|
|
2247
|
+
yield fn();
|
|
2248
|
+
endTimer({ effectName, status: 1 });
|
|
2249
|
+
}
|
|
2250
|
+
catch (error) {
|
|
2251
|
+
endTimer({ effectName, status: 0 });
|
|
2252
|
+
throw error;
|
|
2253
|
+
}
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
trackJob(jobName, handler) {
|
|
2257
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2258
|
+
const endTimer = this.jobHistogram.startTimer();
|
|
2259
|
+
try {
|
|
2260
|
+
yield handler();
|
|
2261
|
+
endTimer({ jobName, status: 1 });
|
|
2262
|
+
}
|
|
2263
|
+
catch (error) {
|
|
2264
|
+
endTimer({ jobName, status: 0 });
|
|
2265
|
+
throw error;
|
|
2266
|
+
}
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
trackEventEmitted(eventType) {
|
|
2270
|
+
this.eventCounter.inc({ eventType });
|
|
2271
|
+
}
|
|
2272
|
+
trackCustomPeriodExpression(customPeriod) {
|
|
2273
|
+
this.customPeriodCounter.inc({ customPeriod });
|
|
2274
|
+
}
|
|
2275
|
+
trackUpdatePlayerJobSource(source) {
|
|
2276
|
+
this.updatePlayerJobSourceCounter.inc({ source });
|
|
2277
|
+
}
|
|
2278
|
+
updateQueueMetrics(queueName, counts) {
|
|
2279
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2280
|
+
for (const [state, count] of Object.entries(counts)) {
|
|
2281
|
+
this.jobQueueGauge.set({ queueName, state }, count);
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
var prometheusService = new PrometheusService();
|
|
2287
|
+
|
|
2060
2288
|
const CUSTOM_PERIOD_REGEX = /(\d+y)?(\d+m)?(\d+w)?(\d+d)?(\d+h)?/;
|
|
2061
2289
|
const PeriodProps = {
|
|
2062
2290
|
[Period.FIVE_MIN]: { name: '5 Min', milliseconds: 300000 },
|
|
@@ -2084,6 +2312,7 @@ function parsePeriodExpression(periodExpression) {
|
|
|
2084
2312
|
durationMs: PeriodProps[fixed].milliseconds
|
|
2085
2313
|
};
|
|
2086
2314
|
}
|
|
2315
|
+
prometheusService.trackCustomPeriodExpression(fixed);
|
|
2087
2316
|
const result = fixed.match(CUSTOM_PERIOD_REGEX);
|
|
2088
2317
|
if (!result || result.length === 0 || result[0] !== fixed)
|
|
2089
2318
|
return null;
|
package/dist/es/index.mjs
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import prometheus from 'prom-client';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import 'dotenv/config';
|
|
5
|
+
import { errored, fromPromise, isErrored, complete } from '@attio/fetchable';
|
|
6
|
+
|
|
1
7
|
var config = {
|
|
2
8
|
defaultUserAgent: `WiseOldMan JS Client v${process.env.npm_package_version}`,
|
|
3
9
|
baseAPIUrl: 'https://api.wiseoldman.net/v2'
|
|
@@ -642,6 +648,8 @@ const PlayerType = {
|
|
|
642
648
|
};
|
|
643
649
|
const PlayerAnnotationType = {
|
|
644
650
|
OPT_OUT: 'opt_out',
|
|
651
|
+
OPT_OUT_GROUPS: 'opt_out_groups',
|
|
652
|
+
OPT_OUT_COMPETITIONS: 'opt_out_competitions',
|
|
645
653
|
BLOCKED: 'blocked',
|
|
646
654
|
FAKE_F2P: 'fake_f2p'
|
|
647
655
|
};
|
|
@@ -2057,6 +2065,226 @@ function getParentEfficiencyMetric(metric) {
|
|
|
2057
2065
|
return null;
|
|
2058
2066
|
}
|
|
2059
2067
|
|
|
2068
|
+
/**
|
|
2069
|
+
* This file has been created as a way to force any usage
|
|
2070
|
+
* of process.env to go through a dotenv.config first.
|
|
2071
|
+
*/
|
|
2072
|
+
/**
|
|
2073
|
+
* This ensures that an env var is required in prod but optional in dev/test.
|
|
2074
|
+
*/
|
|
2075
|
+
function prodOnly(varSchema) {
|
|
2076
|
+
if (process.env.NODE_ENV === 'production') {
|
|
2077
|
+
return varSchema;
|
|
2078
|
+
}
|
|
2079
|
+
return z.optional(varSchema);
|
|
2080
|
+
}
|
|
2081
|
+
const envVariablesSchema = z.object({
|
|
2082
|
+
// Prisma Database URL
|
|
2083
|
+
CORE_DATABASE_URL: z.string().trim().min(1),
|
|
2084
|
+
// Redis Configs
|
|
2085
|
+
REDIS_HOST: z.string().trim().min(1),
|
|
2086
|
+
REDIS_PORT: z.coerce.number().positive().int(),
|
|
2087
|
+
// Node Environment
|
|
2088
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
2089
|
+
// Port for the API to run on
|
|
2090
|
+
API_PORT: z.optional(z.coerce.number().positive().int()),
|
|
2091
|
+
// Admin Password (For mod+ operations)
|
|
2092
|
+
ADMIN_PASSWORD: prodOnly(z.string().trim().min(1)),
|
|
2093
|
+
// Sentry (for error tracking)
|
|
2094
|
+
API_SENTRY_DSN: prodOnly(z.string().trim().min(1)),
|
|
2095
|
+
// Patreon Token (to access their API)
|
|
2096
|
+
PATREON_BEARER_TOKEN: prodOnly(z.string().trim().min(1)),
|
|
2097
|
+
// Discord Bot API URL (to send events to)
|
|
2098
|
+
DISCORD_BOT_API_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2099
|
+
// Our Prometheus metrics aggregator service URL
|
|
2100
|
+
PROMETHEUS_METRICS_SERVICE_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2101
|
+
// Discord Monitoring Webhooks
|
|
2102
|
+
DISCORD_PATREON_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2103
|
+
DISCORD_MONITORING_WEBHOOK_URL: prodOnly(z.string().trim().min(1).url()),
|
|
2104
|
+
// Proxy Configs
|
|
2105
|
+
PROXY_LIST: prodOnly(z.string().trim().min(1)),
|
|
2106
|
+
PROXY_USER: prodOnly(z.string().trim().min(1)),
|
|
2107
|
+
PROXY_PASSWORD: prodOnly(z.string().trim().min(1)),
|
|
2108
|
+
PROXY_PORT: prodOnly(z.coerce.number().positive().int()),
|
|
2109
|
+
CPU_COUNT: prodOnly(z.coerce.number().positive().int()),
|
|
2110
|
+
// Openai API Key
|
|
2111
|
+
OPENAI_API_KEY: prodOnly(z.string().trim().min(1).startsWith('sk-'))
|
|
2112
|
+
});
|
|
2113
|
+
// This will load env vars from a .env file, type check them,and throw an error
|
|
2114
|
+
// (interrupting the process) if they're required and missing, or of an invalid type.
|
|
2115
|
+
try {
|
|
2116
|
+
envVariablesSchema.parse(process.env);
|
|
2117
|
+
}
|
|
2118
|
+
catch (error) {
|
|
2119
|
+
const errorPayload = JSON.stringify(error, null, 2);
|
|
2120
|
+
throw new Error(`Invalid environment variables. Please check env.ts for more info.\n${errorPayload}`);
|
|
2121
|
+
}
|
|
2122
|
+
function getThreadIndex() {
|
|
2123
|
+
if (process.env.pm_id === undefined) {
|
|
2124
|
+
return null;
|
|
2125
|
+
}
|
|
2126
|
+
return parseInt(process.env.pm_id, 10);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
class PrometheusService {
|
|
2130
|
+
constructor() {
|
|
2131
|
+
this.pushInterval = null;
|
|
2132
|
+
this.registry = new prometheus.Registry();
|
|
2133
|
+
this.registry.setDefaultLabels({ app: 'wise-old-man', threadIndex: getThreadIndex() });
|
|
2134
|
+
prometheus.collectDefaultMetrics({ register: this.registry });
|
|
2135
|
+
this.effectHistogram = new prometheus.Histogram({
|
|
2136
|
+
name: 'effect_duration_seconds',
|
|
2137
|
+
help: 'Duration of effects in microseconds',
|
|
2138
|
+
labelNames: ['effectName', 'status'],
|
|
2139
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
|
|
2140
|
+
});
|
|
2141
|
+
this.httpHistogram = new prometheus.Histogram({
|
|
2142
|
+
name: 'http_request_duration_seconds',
|
|
2143
|
+
help: 'Duration of HTTP requests in microseconds',
|
|
2144
|
+
labelNames: ['method', 'route', 'status', 'userAgent'],
|
|
2145
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10, 30]
|
|
2146
|
+
});
|
|
2147
|
+
this.jobHistogram = new prometheus.Histogram({
|
|
2148
|
+
name: 'job_duration_seconds',
|
|
2149
|
+
help: 'Duration of jobs in microseconds',
|
|
2150
|
+
labelNames: ['jobName', 'status'],
|
|
2151
|
+
buckets: [0.1, 0.5, 1, 5, 10, 30, 60]
|
|
2152
|
+
});
|
|
2153
|
+
this.jobQueueGauge = new prometheus.Gauge({
|
|
2154
|
+
name: 'job_queue_size',
|
|
2155
|
+
help: 'Number of jobs in different states for each queue',
|
|
2156
|
+
labelNames: ['queueName', 'state']
|
|
2157
|
+
});
|
|
2158
|
+
this.eventCounter = new prometheus.Counter({
|
|
2159
|
+
name: 'event_counter',
|
|
2160
|
+
help: 'Count of events emitted',
|
|
2161
|
+
labelNames: ['eventType']
|
|
2162
|
+
});
|
|
2163
|
+
this.customPeriodCounter = new prometheus.Counter({
|
|
2164
|
+
name: 'custom_period_counter',
|
|
2165
|
+
help: 'Count of custom period expressions used',
|
|
2166
|
+
labelNames: ['customPeriod']
|
|
2167
|
+
});
|
|
2168
|
+
this.updatePlayerJobSourceCounter = new prometheus.Counter({
|
|
2169
|
+
name: 'update_player_job_source_counter',
|
|
2170
|
+
help: 'Count of update player jobs dispatched',
|
|
2171
|
+
labelNames: ['source']
|
|
2172
|
+
});
|
|
2173
|
+
this.runeMetricsHistogram = new prometheus.Histogram({
|
|
2174
|
+
name: 'runemetrics_duration_seconds',
|
|
2175
|
+
help: 'Duration of RuneMetrics requests in microseconds',
|
|
2176
|
+
labelNames: ['status'],
|
|
2177
|
+
buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
|
|
2178
|
+
});
|
|
2179
|
+
this.hiscoresHistogram = new prometheus.Histogram({
|
|
2180
|
+
name: 'hiscores_duration_seconds',
|
|
2181
|
+
help: 'Duration of hiscores requests in microseconds',
|
|
2182
|
+
labelNames: ['status'],
|
|
2183
|
+
buckets: [0.1, 0.3, 0.5, 1, 5, 10, 30]
|
|
2184
|
+
});
|
|
2185
|
+
this.registry.registerMetric(this.jobHistogram);
|
|
2186
|
+
this.registry.registerMetric(this.jobQueueGauge);
|
|
2187
|
+
this.registry.registerMetric(this.httpHistogram);
|
|
2188
|
+
this.registry.registerMetric(this.effectHistogram);
|
|
2189
|
+
this.registry.registerMetric(this.eventCounter);
|
|
2190
|
+
this.registry.registerMetric(this.customPeriodCounter);
|
|
2191
|
+
this.registry.registerMetric(this.updatePlayerJobSourceCounter);
|
|
2192
|
+
this.registry.registerMetric(this.runeMetricsHistogram);
|
|
2193
|
+
this.registry.registerMetric(this.hiscoresHistogram);
|
|
2194
|
+
}
|
|
2195
|
+
init() {
|
|
2196
|
+
this.pushInterval = setInterval(() => {
|
|
2197
|
+
this.pushMetrics();
|
|
2198
|
+
}, 60000);
|
|
2199
|
+
}
|
|
2200
|
+
shutdown() {
|
|
2201
|
+
if (this.pushInterval !== null) {
|
|
2202
|
+
clearInterval(this.pushInterval);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
pushMetrics() {
|
|
2206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2207
|
+
if (process.env.NODE_ENV === 'test') {
|
|
2208
|
+
return errored({ code: 'NOT_ALLOWED_IN_TEST_ENV' });
|
|
2209
|
+
}
|
|
2210
|
+
if (!process.env.PROMETHEUS_METRICS_SERVICE_URL) {
|
|
2211
|
+
return errored({ code: 'MISSING_METRICS_URL' });
|
|
2212
|
+
}
|
|
2213
|
+
const metricsResult = yield fromPromise(this.registry.getMetricsAsJSON());
|
|
2214
|
+
if (isErrored(metricsResult)) {
|
|
2215
|
+
return errored({
|
|
2216
|
+
code: 'FAILED_TO_GET_PROMETHEUS_METRICS',
|
|
2217
|
+
subError: metricsResult.error
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
const requestResult = yield fromPromise(axios.post(process.env.PROMETHEUS_METRICS_SERVICE_URL, {
|
|
2221
|
+
source: 'api',
|
|
2222
|
+
data: metricsResult.value,
|
|
2223
|
+
threadIndex: getThreadIndex()
|
|
2224
|
+
}));
|
|
2225
|
+
if (isErrored(requestResult)) {
|
|
2226
|
+
return errored({
|
|
2227
|
+
code: 'FAILED_TO_PUSH_PROMETHEUS_METRICS',
|
|
2228
|
+
subError: requestResult.error
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
return complete(true);
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
2234
|
+
trackHttpRequest() {
|
|
2235
|
+
return this.httpHistogram.startTimer();
|
|
2236
|
+
}
|
|
2237
|
+
trackRuneMetricsRequest() {
|
|
2238
|
+
return this.runeMetricsHistogram.startTimer();
|
|
2239
|
+
}
|
|
2240
|
+
trackHiscoresRequest() {
|
|
2241
|
+
return this.hiscoresHistogram.startTimer();
|
|
2242
|
+
}
|
|
2243
|
+
trackEffect(effectName, fn) {
|
|
2244
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2245
|
+
const endTimer = this.effectHistogram.startTimer();
|
|
2246
|
+
try {
|
|
2247
|
+
yield fn();
|
|
2248
|
+
endTimer({ effectName, status: 1 });
|
|
2249
|
+
}
|
|
2250
|
+
catch (error) {
|
|
2251
|
+
endTimer({ effectName, status: 0 });
|
|
2252
|
+
throw error;
|
|
2253
|
+
}
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
trackJob(jobName, handler) {
|
|
2257
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2258
|
+
const endTimer = this.jobHistogram.startTimer();
|
|
2259
|
+
try {
|
|
2260
|
+
yield handler();
|
|
2261
|
+
endTimer({ jobName, status: 1 });
|
|
2262
|
+
}
|
|
2263
|
+
catch (error) {
|
|
2264
|
+
endTimer({ jobName, status: 0 });
|
|
2265
|
+
throw error;
|
|
2266
|
+
}
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
trackEventEmitted(eventType) {
|
|
2270
|
+
this.eventCounter.inc({ eventType });
|
|
2271
|
+
}
|
|
2272
|
+
trackCustomPeriodExpression(customPeriod) {
|
|
2273
|
+
this.customPeriodCounter.inc({ customPeriod });
|
|
2274
|
+
}
|
|
2275
|
+
trackUpdatePlayerJobSource(source) {
|
|
2276
|
+
this.updatePlayerJobSourceCounter.inc({ source });
|
|
2277
|
+
}
|
|
2278
|
+
updateQueueMetrics(queueName, counts) {
|
|
2279
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2280
|
+
for (const [state, count] of Object.entries(counts)) {
|
|
2281
|
+
this.jobQueueGauge.set({ queueName, state }, count);
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
var prometheusService = new PrometheusService();
|
|
2287
|
+
|
|
2060
2288
|
const CUSTOM_PERIOD_REGEX = /(\d+y)?(\d+m)?(\d+w)?(\d+d)?(\d+h)?/;
|
|
2061
2289
|
const PeriodProps = {
|
|
2062
2290
|
[Period.FIVE_MIN]: { name: '5 Min', milliseconds: 300000 },
|
|
@@ -2084,6 +2312,7 @@ function parsePeriodExpression(periodExpression) {
|
|
|
2084
2312
|
durationMs: PeriodProps[fixed].milliseconds
|
|
2085
2313
|
};
|
|
2086
2314
|
}
|
|
2315
|
+
prometheusService.trackCustomPeriodExpression(fixed);
|
|
2087
2316
|
const result = fixed.match(CUSTOM_PERIOD_REGEX);
|
|
2088
2317
|
if (!result || result.length === 0 || result[0] !== fixed)
|
|
2089
2318
|
return null;
|
package/dist/index.d.ts
CHANGED
|
@@ -642,6 +642,8 @@ declare const PlayerType: {
|
|
|
642
642
|
type PlayerType = (typeof PlayerType)[keyof typeof PlayerType];
|
|
643
643
|
declare const PlayerAnnotationType: {
|
|
644
644
|
readonly OPT_OUT: "opt_out";
|
|
645
|
+
readonly OPT_OUT_GROUPS: "opt_out_groups";
|
|
646
|
+
readonly OPT_OUT_COMPETITIONS: "opt_out_competitions";
|
|
645
647
|
readonly BLOCKED: "blocked";
|
|
646
648
|
readonly FAKE_F2P: "fake_f2p";
|
|
647
649
|
};
|
|
@@ -1404,9 +1406,6 @@ interface GroupStatistics {
|
|
|
1404
1406
|
averageStats: FormattedSnapshot;
|
|
1405
1407
|
metricLeaders: MetricLeaders;
|
|
1406
1408
|
}
|
|
1407
|
-
type MemberRoleChangeEvent = Omit<MemberActivity, 'createdAt'>;
|
|
1408
|
-
type MemberJoinedEvent = Omit<MemberActivity, 'createdAt' | 'previousRole'>;
|
|
1409
|
-
type MemberLeftEvent = Omit<MemberActivity, 'createdAt' | 'previousRole'>;
|
|
1410
1409
|
type MemberActivityWithPlayer = MemberActivity & {
|
|
1411
1410
|
player: Player;
|
|
1412
1411
|
};
|
|
@@ -2284,4 +2283,4 @@ declare class WOMClient extends BaseAPIClient {
|
|
|
2284
2283
|
constructor(options?: WOMClientOptions);
|
|
2285
2284
|
}
|
|
2286
2285
|
|
|
2287
|
-
export { ACTIVITIES, type Achievement, type AchievementDefinition, type AchievementProgress, type AchievementTemplate, Activity, type ActivityDelta, ActivityType, type ActivityValue, type AssertPlayerTypeResponse, BOSSES, type Bonus, Boss, type BossDelta, type BossMetaConfig, type BossValue, CAPPED_MAX_TOTAL_XP, COMBAT_SKILLS, COMPETITION_STATUSES, COMPETITION_TYPES, COMPUTED_METRICS, COUNTRY_CODES, type ChangeMemberRolePayload, CompetitionCSVTableType, type CompetitionDetails, type CompetitionDetailsCSVParams, type CompetitionListItem, CompetitionStatus, CompetitionStatusProps, CompetitionType, CompetitionTypeProps, type CompetitionWithParticipations, type CompetitionsSearchFilter, ComputedMetric, type ComputedMetricDelta, type ComputedMetricValue, Country, type CountryDetails, CountryProps, type CreateCompetitionPayload, type CreateCompetitionResponse, type CreateGroupPayload, type CreateGroupResponse, type DeltaGroupLeaderboardEntry, type DeltaLeaderboardEntry, type DeltaLeaderboardFilter, type DenyContext, type EditCompetitionPayload, type EditGroupPayload, EfficiencyAlgorithmType, type EfficiencyAlgorithmTypeUnion, type EfficiencyLeaderboardsFilter, type ExtendedAchievement, type ExtendedAchievementWithPlayer, F2P_BOSSES, type FlaggedPlayerReviewContext, type FormattedSnapshot, GROUP_ROLES, type GenericCountMessageResponse, type GenericMessageResponse, type GetGroupGainsFilter, type GetPlayerGainsResponse, type Group, type GroupDetails, type GroupHiscoresActivityItem, type GroupHiscoresBossItem, type GroupHiscoresComputedMetricItem, type GroupHiscoresEntry, type GroupHiscoresSkillItem, type GroupListItem, type GroupMemberFragment, type GroupRecordsFilter, GroupRole, GroupRoleProps, type GroupStatistics, MAX_LEVEL, MAX_SKILL_EXP, MAX_VIRTUAL_LEVEL, MEMBER_SKILLS, METRICS, type MapOf, type MeasuredDeltaProgress, type MemberActivityWithPlayer, type MemberInput, type
|
|
2286
|
+
export { ACTIVITIES, type Achievement, type AchievementDefinition, type AchievementProgress, type AchievementTemplate, Activity, type ActivityDelta, ActivityType, type ActivityValue, type AssertPlayerTypeResponse, BOSSES, type Bonus, Boss, type BossDelta, type BossMetaConfig, type BossValue, CAPPED_MAX_TOTAL_XP, COMBAT_SKILLS, COMPETITION_STATUSES, COMPETITION_TYPES, COMPUTED_METRICS, COUNTRY_CODES, type ChangeMemberRolePayload, CompetitionCSVTableType, type CompetitionDetails, type CompetitionDetailsCSVParams, type CompetitionListItem, CompetitionStatus, CompetitionStatusProps, CompetitionType, CompetitionTypeProps, type CompetitionWithParticipations, type CompetitionsSearchFilter, ComputedMetric, type ComputedMetricDelta, type ComputedMetricValue, Country, type CountryDetails, CountryProps, type CreateCompetitionPayload, type CreateCompetitionResponse, type CreateGroupPayload, type CreateGroupResponse, type DeltaGroupLeaderboardEntry, type DeltaLeaderboardEntry, type DeltaLeaderboardFilter, type DenyContext, type EditCompetitionPayload, type EditGroupPayload, EfficiencyAlgorithmType, type EfficiencyAlgorithmTypeUnion, type EfficiencyLeaderboardsFilter, type ExtendedAchievement, type ExtendedAchievementWithPlayer, F2P_BOSSES, type FlaggedPlayerReviewContext, type FormattedSnapshot, GROUP_ROLES, type GenericCountMessageResponse, type GenericMessageResponse, type GetGroupGainsFilter, type GetPlayerGainsResponse, type Group, type GroupDetails, type GroupHiscoresActivityItem, type GroupHiscoresBossItem, type GroupHiscoresComputedMetricItem, type GroupHiscoresEntry, type GroupHiscoresSkillItem, type GroupListItem, type GroupMemberFragment, type GroupRecordsFilter, GroupRole, GroupRoleProps, type GroupStatistics, MAX_LEVEL, MAX_SKILL_EXP, MAX_VIRTUAL_LEVEL, MEMBER_SKILLS, METRICS, type MapOf, type MeasuredDeltaProgress, type MemberActivityWithPlayer, type MemberInput, type MembershipWithGroup, type MembershipWithPlayer, Metric, type MetricLeaders, MetricMeasure, MetricProps, MetricType, type MetricValueKey, type NameChange, type NameChangeDetails, NameChangeStatus, type NameChangeWithPlayer, type NameChangesSearchFilter, PERIODS, PLAYER_BUILDS, PLAYER_STATUSES, PLAYER_TYPES, PRIVELEGED_GROUP_ROLES, type ParticipationWithCompetition, type ParticipationWithCompetitionAndStandings, type ParticipationWithPlayer, type ParticipationWithPlayerAndProgress, Period, PeriodProps, type Player, PlayerAnnotationType, type PlayerArchiveWithPlayer, PlayerBuild, PlayerBuildProps, type PlayerCompetitionStandingsFilter, type PlayerCompetitionsFilter, type PlayerDeltasMap, type PlayerDetails, type PlayerRecordsFilter, PlayerStatus, PlayerStatusProps, PlayerType, PlayerTypeProps, REAL_METRICS, REAL_SKILLS, type Record, type RecordLeaderboardEntry, type RecordLeaderboardFilter, SKILLS, SKILL_EXP_AT_99, Skill, type SkillDelta, type SkillMetaConfig, type SkillMetaMethod, type SkillValue, type SkipContext, type Snapshot, type SnapshotFragment, type Team, type TimeRangeFilter, type Top5ProgressResult, WOMClient, findCountry, findCountryByCode, findCountryByName, findGroupRole, findMetric, findPeriod, findPlayerBuild, findPlayerType, formatNumber, getCombatLevel, getExpForLevel, getLevel, getMetricMeasure, getMetricName, getMetricRankKey, getMetricValueKey, getMinimumValue, getParentEfficiencyMetric, isActivity, isBoss, isCompetitionStatus, isCompetitionType, isComputedMetric, isCountry, isGroupRole, isMetric, isPeriod, isPlayerBuild, isPlayerStatus, isPlayerType, isSkill, padNumber, parsePeriodExpression, round };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wise-old-man/utils",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.12",
|
|
4
4
|
"description": "A JavaScript/TypeScript client that interfaces and consumes the Wise Old Man API, an API that tracks and measures players' progress in Old School Runescape.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wiseoldman",
|