denotify-client 1.0.0 → 1.1.2
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/denotifyclient.js +7 -0
- package/dist/examples/aave-healthcheck.js +33 -36
- package/dist/examples/delete-alert.js +2 -4
- package/dist/index.js +263 -0
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +259 -1
- package/dist/notifications/index.js +3 -0
- package/dist/triggers/handler_function_call.js +12 -1
- package/dist/triggers/handler_onchain_event.js +12 -1
- package/dist/triggers/index.js +5 -0
- package/dist/triggers/trigger.js +4 -0
- package/package.json +5 -4
- package/types/denotifyclient.d.ts +1 -0
- package/types/functionbuilder.d.ts +1 -1
- package/types/index.d.ts +6 -1
- package/types/notifications/index.d.ts +3 -0
- package/types/notifications/notification.d.ts +7 -7
- package/types/notifications/notify_discord_webhook.d.ts +5 -5
- package/types/triggers/handler_function_call.d.ts +19 -5
- package/types/triggers/handler_function_call_v2.d.ts +6 -6
- package/types/triggers/handler_onchain_event.d.ts +17 -5
- package/types/triggers/index.d.ts +5 -0
- package/types/triggers/trigger.d.ts +8 -8
- package/types/types/types.d.ts +4 -4
- package/types/util/filter.d.ts +9 -9
package/dist/denotifyclient.js
CHANGED
@@ -96,12 +96,19 @@ export class DeNotifyClient {
|
|
96
96
|
return ret.hash;
|
97
97
|
}
|
98
98
|
async setAlertName(triggerId, name) {
|
99
|
+
throw new Error('Not yet supported - Sorry!');
|
99
100
|
}
|
100
101
|
async enableAlert(triggerId) {
|
102
|
+
throw new Error('Not yet supported - Sorry!');
|
101
103
|
}
|
102
104
|
async disableAlert(triggerId) {
|
105
|
+
throw new Error('Not yet supported - Sorry!');
|
106
|
+
}
|
107
|
+
async updateNotification(triggerId) {
|
108
|
+
throw new Error('Not yet supported - Sorry!');
|
103
109
|
}
|
104
110
|
async updateTrigger(triggerId, update) {
|
111
|
+
// TODO - Input validation
|
105
112
|
const ret = await this.request('patch', `alerts/trigger-handler/${triggerId}`, { body: update });
|
106
113
|
return ret;
|
107
114
|
}
|
@@ -1,50 +1,47 @@
|
|
1
|
+
import { AlertBuilder } from "../alertbuilder.js";
|
1
2
|
import { DeNotifyClient } from "../denotifyclient.js";
|
3
|
+
import { FunctionBuilder } from "../functionbuilder.js";
|
2
4
|
import { FilterBuilder } from "../util/filter.js";
|
3
5
|
// Simple App to demonstrate usage. Created a balance monitoring alert, updates it and deletes it
|
4
6
|
async function main() {
|
5
7
|
const api = await DeNotifyClient.create({
|
6
|
-
email:
|
7
|
-
password:
|
8
|
-
projectId: 'xfxplbmdcoukaitzxzei',
|
9
|
-
anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhmeHBsYm1kY291a2FpdHp4emVpIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzgwMDg4NzMsImV4cCI6MTk5MzU4NDg3M30.WLk7bR5syQ4YJ8_jNOAuaT1UMvl7E2MS_VYMs7sN56c'
|
8
|
+
email: process.env.EMAIL,
|
9
|
+
password: process.env.PASSWORD,
|
10
10
|
});
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
//
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
//
|
33
|
-
|
34
|
-
|
35
|
-
//
|
36
|
-
|
37
|
-
// console.log(triggerId)
|
38
|
-
// // Update the period to every 10s
|
39
|
-
// await api.updateTrigger(triggerId, { timePeriod: '10s' })
|
11
|
+
const network = 'avalanche';
|
12
|
+
const address = '0x26985888d5b7019ff2A7444fB567D8F386c3b538';
|
13
|
+
const myAddress = '0x7601630eC802952ba1ED2B6e4db16F699A0a5A87';
|
14
|
+
const { abi } = await api.getAbi(network, address);
|
15
|
+
const webhook = process.env.DISCORD_WEBHOOK;
|
16
|
+
const builder = FunctionBuilder.create(api);
|
17
|
+
await builder.addFunction(address, 'getBalance', [myAddress], abi);
|
18
|
+
// Create the Balance Monitor alert
|
19
|
+
const alert = AlertBuilder.create('Test Alert')
|
20
|
+
.onNetwork('avalanche')
|
21
|
+
.withTrigger('PollFunctionV2', {
|
22
|
+
timeBase: 'time',
|
23
|
+
timePeriod: '100s',
|
24
|
+
functions: builder.get(),
|
25
|
+
triggerOn: 'always',
|
26
|
+
})
|
27
|
+
.withNotification('Discord', {
|
28
|
+
url: webhook,
|
29
|
+
message: `Your avax balance is [{func_0_ret_0 / 1e18}](https://snowtrace.io/address/${myAddress})`,
|
30
|
+
})
|
31
|
+
.config();
|
32
|
+
// Create the alert with the API
|
33
|
+
const triggerId = await api.createAlert(alert);
|
34
|
+
console.log(triggerId);
|
35
|
+
// Update the period to every 10s
|
36
|
+
await api.updateTrigger(triggerId, { timePeriod: '10s' });
|
40
37
|
// Update the Filter using the filter builder
|
41
38
|
const filter = FilterBuilder.new()
|
42
39
|
.addCondition('WHERE', 'func_0_ret_0', 'Number', 'gt', 3)
|
43
40
|
.finalise();
|
44
41
|
await api.updateTrigger(13, { triggerOn: 'filter', filter, filterVersion: FilterBuilder.version() });
|
45
42
|
// Delete the filter in 10s
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
setTimeout(async () => {
|
44
|
+
await api.deleteAlert(triggerId);
|
45
|
+
}, 10 * 1000);
|
49
46
|
}
|
50
47
|
main();
|
@@ -2,10 +2,8 @@ import { DeNotifyClient } from "../denotifyclient.js";
|
|
2
2
|
// Simple App to demonstrate deleting an alert
|
3
3
|
async function main() {
|
4
4
|
const api = await DeNotifyClient.create({
|
5
|
-
email:
|
6
|
-
password:
|
7
|
-
projectId: 'xfxplbmdcoukaitzxzei',
|
8
|
-
anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhmeHBsYm1kY291a2FpdHp4emVpIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzgwMDg4NzMsImV4cCI6MTk5MzU4NDg3M30.WLk7bR5syQ4YJ8_jNOAuaT1UMvl7E2MS_VYMs7sN56c'
|
5
|
+
email: process.env.EMAIL,
|
6
|
+
password: process.env.PASSWORD,
|
9
7
|
});
|
10
8
|
const alertId = 11;
|
11
9
|
await api.deleteAlert(alertId);
|
package/dist/index.js
CHANGED
@@ -11,6 +11,7 @@ var followRedirects = require('follow-redirects');
|
|
11
11
|
var zlib = require('zlib');
|
12
12
|
var stream = require('stream');
|
13
13
|
var EventEmitter = require('events');
|
14
|
+
var ethers = require('ethers');
|
14
15
|
|
15
16
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
16
17
|
|
@@ -4084,6 +4085,32 @@ class Notification {
|
|
4084
4085
|
}
|
4085
4086
|
}
|
4086
4087
|
|
4088
|
+
const HANDLER_FUNCTION_CALL_V1_RAW_ID = 'handler_function_call';
|
4089
|
+
class HandlerFunctionCall {
|
4090
|
+
static SimpleToRaw(name, network, config) {
|
4091
|
+
return {
|
4092
|
+
alertType: 'event',
|
4093
|
+
network,
|
4094
|
+
nickname: name,
|
4095
|
+
type: HANDLER_FUNCTION_CALL_V1_RAW_ID,
|
4096
|
+
handler: config
|
4097
|
+
};
|
4098
|
+
}
|
4099
|
+
}
|
4100
|
+
|
4101
|
+
const HANDLER_ONCHAIN_EVENT_V1_RAW_ID = 'handler_onchain_event';
|
4102
|
+
class HandlerOnchainEvent {
|
4103
|
+
static SimpleToRaw(name, network, config) {
|
4104
|
+
return {
|
4105
|
+
alertType: 'event',
|
4106
|
+
network,
|
4107
|
+
nickname: name,
|
4108
|
+
type: HANDLER_ONCHAIN_EVENT_V1_RAW_ID,
|
4109
|
+
handler: config
|
4110
|
+
};
|
4111
|
+
}
|
4112
|
+
}
|
4113
|
+
|
4087
4114
|
const HANDLER_FUNCTION_CALL_V2_RAW_ID = 'handler_function_call_v2';
|
4088
4115
|
class HandlerFunctionCallV2 {
|
4089
4116
|
static SimpleToRaw(name, network, config) {
|
@@ -4101,6 +4128,8 @@ class Trigger {
|
|
4101
4128
|
static SimpleToRaw(name, id, network, config) {
|
4102
4129
|
switch (id) {
|
4103
4130
|
case 'PollFunctionV2': return HandlerFunctionCallV2.SimpleToRaw(name, network, config);
|
4131
|
+
case 'PollFunctionV1': return HandlerFunctionCall.SimpleToRaw(name, network, config);
|
4132
|
+
case 'OnchainEventV2': return HandlerOnchainEvent.SimpleToRaw(name, network, config);
|
4104
4133
|
default:
|
4105
4134
|
throw new Error('Invalid Trigger ID');
|
4106
4135
|
}
|
@@ -4201,15 +4230,249 @@ class DeNotifyClient {
|
|
4201
4230
|
return ret.hash;
|
4202
4231
|
}
|
4203
4232
|
async setAlertName(triggerId, name) {
|
4233
|
+
throw new Error('Not yet supported - Sorry!');
|
4204
4234
|
}
|
4205
4235
|
async enableAlert(triggerId) {
|
4236
|
+
throw new Error('Not yet supported - Sorry!');
|
4206
4237
|
}
|
4207
4238
|
async disableAlert(triggerId) {
|
4239
|
+
throw new Error('Not yet supported - Sorry!');
|
4240
|
+
}
|
4241
|
+
async updateNotification(triggerId) {
|
4242
|
+
throw new Error('Not yet supported - Sorry!');
|
4208
4243
|
}
|
4209
4244
|
async updateTrigger(triggerId, update) {
|
4245
|
+
// TODO - Input validation
|
4210
4246
|
const ret = await this.request('patch', `alerts/trigger-handler/${triggerId}`, { body: update });
|
4211
4247
|
return ret;
|
4212
4248
|
}
|
4213
4249
|
}
|
4214
4250
|
|
4251
|
+
class AlertBuilder {
|
4252
|
+
name;
|
4253
|
+
network;
|
4254
|
+
triggerId;
|
4255
|
+
trigger;
|
4256
|
+
notificationId;
|
4257
|
+
notification;
|
4258
|
+
constructor(name) {
|
4259
|
+
this.name = name;
|
4260
|
+
}
|
4261
|
+
static create(name) {
|
4262
|
+
return new AlertBuilder(name);
|
4263
|
+
}
|
4264
|
+
onNetwork(network) {
|
4265
|
+
this.network = network;
|
4266
|
+
return this;
|
4267
|
+
}
|
4268
|
+
withTrigger(id, options) {
|
4269
|
+
this.triggerId = id;
|
4270
|
+
this.trigger = options;
|
4271
|
+
return this;
|
4272
|
+
}
|
4273
|
+
withNotification(id, options) {
|
4274
|
+
this.notificationId = id;
|
4275
|
+
this.notification = options;
|
4276
|
+
return this;
|
4277
|
+
}
|
4278
|
+
config() {
|
4279
|
+
if (this.trigger === undefined || this.triggerId === undefined)
|
4280
|
+
throw new Error('Trigger not configured');
|
4281
|
+
if (this.notification === undefined || this.notificationId === undefined)
|
4282
|
+
throw new Error('Notification not configured');
|
4283
|
+
if (this.network === undefined)
|
4284
|
+
throw new Error('Network not configured');
|
4285
|
+
return {
|
4286
|
+
name: this.name,
|
4287
|
+
network: this.network,
|
4288
|
+
triggerId: this.triggerId,
|
4289
|
+
trigger: this.trigger,
|
4290
|
+
notificationId: this.notificationId,
|
4291
|
+
notification: this.notification,
|
4292
|
+
};
|
4293
|
+
}
|
4294
|
+
}
|
4295
|
+
|
4296
|
+
class FunctionBuilder {
|
4297
|
+
api;
|
4298
|
+
data = [];
|
4299
|
+
constructor(api) {
|
4300
|
+
this.api = api;
|
4301
|
+
}
|
4302
|
+
static create(api) {
|
4303
|
+
return new FunctionBuilder(api);
|
4304
|
+
}
|
4305
|
+
getAbiHash(abi) {
|
4306
|
+
if (this.api === undefined)
|
4307
|
+
throw new Error('FunctionBuilder must be initialised with api');
|
4308
|
+
return this.api.getAbiHash(abi);
|
4309
|
+
}
|
4310
|
+
async addFunction(address, func, args, abi) {
|
4311
|
+
const contract = new ethers.ethers.Contract(address, abi);
|
4312
|
+
contract.interface.encodeFunctionData(func, args);
|
4313
|
+
this.data.push({
|
4314
|
+
address,
|
4315
|
+
bytecode: contract.interface.encodeFunctionData(func, args),
|
4316
|
+
abiHash: await this.getAbiHash(abi),
|
4317
|
+
function: func,
|
4318
|
+
});
|
4319
|
+
return this;
|
4320
|
+
}
|
4321
|
+
get() {
|
4322
|
+
return this.data;
|
4323
|
+
}
|
4324
|
+
}
|
4325
|
+
|
4326
|
+
class Filter {
|
4327
|
+
static version() {
|
4328
|
+
return 'v1';
|
4329
|
+
}
|
4330
|
+
static execute(record, groupsIn, version = 'v1') {
|
4331
|
+
// Deep copy to so we can edit the object during recursion
|
4332
|
+
const groups = JSON.parse(JSON.stringify(groupsIn));
|
4333
|
+
let lhs = true;
|
4334
|
+
for (const group of groups) {
|
4335
|
+
const rhs = Filter.process(record, group.conditions);
|
4336
|
+
lhs = Filter.execLogic(lhs, group.logic, rhs);
|
4337
|
+
}
|
4338
|
+
return lhs;
|
4339
|
+
}
|
4340
|
+
static process(record, conditions, lhs) {
|
4341
|
+
const condition = conditions.shift();
|
4342
|
+
if (!condition) {
|
4343
|
+
if (lhs === undefined)
|
4344
|
+
return true;
|
4345
|
+
return lhs;
|
4346
|
+
}
|
4347
|
+
// lhs <logic> rhs
|
4348
|
+
const rhs = Filter.execCondition(record, condition);
|
4349
|
+
if (lhs === undefined || condition.logic === 'WHERE')
|
4350
|
+
return Filter.process(record, conditions, rhs);
|
4351
|
+
if (condition.logic === undefined)
|
4352
|
+
throw new Error('Invalid filter. Condition must have logic value');
|
4353
|
+
const next = Filter.execLogic(lhs, condition.logic, rhs);
|
4354
|
+
return Filter.process(record, conditions, next);
|
4355
|
+
}
|
4356
|
+
static execLogic(lhs, logic, rhs) {
|
4357
|
+
switch (logic) {
|
4358
|
+
case 'AND': return lhs && rhs;
|
4359
|
+
case 'OR': return lhs || rhs;
|
4360
|
+
case 'XOR': return (lhs || rhs) && !(lhs && rhs);
|
4361
|
+
case 'NAND': return !(lhs && rhs);
|
4362
|
+
case 'NOR': return !(lhs && rhs);
|
4363
|
+
case 'WHERE': return rhs;
|
4364
|
+
default: throw new Error(`Invalid Filter. Bad logic value: ${logic}`);
|
4365
|
+
}
|
4366
|
+
}
|
4367
|
+
static execCondition(record, condition) {
|
4368
|
+
const data = record[condition.key];
|
4369
|
+
if (condition.type === 'Number') {
|
4370
|
+
if (typeof condition.constant !== 'number' || typeof data !== 'number')
|
4371
|
+
throw new Error(`Invalid filter. Type miss-match. Type: ${condition.type}, Variable: ${condition.constant}, Data: ${data}`);
|
4372
|
+
const n = data;
|
4373
|
+
const constant = condition.constant;
|
4374
|
+
switch (condition.operation) {
|
4375
|
+
case 'eq': return n === constant;
|
4376
|
+
case '!eq': return n !== constant;
|
4377
|
+
case 'gt': return n > constant;
|
4378
|
+
case 'gte': return n >= constant;
|
4379
|
+
case 'lt': return n < constant;
|
4380
|
+
case 'lte': return n <= constant;
|
4381
|
+
default: throw new Error('Invalid Filter. Operation does not match type');
|
4382
|
+
}
|
4383
|
+
}
|
4384
|
+
if (condition.type === 'String') {
|
4385
|
+
if (typeof condition.constant !== 'string' || typeof data !== 'string')
|
4386
|
+
throw new Error(`Invalid filter. Type miss-match. Type: ${condition.type}, Variable: ${condition.constant}, Data: ${data}`);
|
4387
|
+
const str = data;
|
4388
|
+
const constant = condition.constant;
|
4389
|
+
switch (condition.operation) {
|
4390
|
+
case 'contains': return str.includes(constant);
|
4391
|
+
case '!contains': return !str.includes(constant);
|
4392
|
+
case 'is': return str === constant;
|
4393
|
+
case '!is': return str !== constant;
|
4394
|
+
case 'isEmpty': return str === '';
|
4395
|
+
case '!isEmpty': return str !== '';
|
4396
|
+
default: throw new Error('Invalid Filter. Operation does not match type');
|
4397
|
+
}
|
4398
|
+
}
|
4399
|
+
if (condition.type === 'Address') {
|
4400
|
+
if (typeof condition.constant !== 'string' || typeof data !== 'string')
|
4401
|
+
throw new Error(`Invalid filter. Type miss-match. Type: ${condition.type}, Variable: ${condition.constant}, Data: ${data}`);
|
4402
|
+
const str = data;
|
4403
|
+
const constant = condition.constant;
|
4404
|
+
switch (condition.operation) {
|
4405
|
+
case 'contains': return str.toLowerCase().includes(constant.toLowerCase());
|
4406
|
+
case '!contains': return !str.toLowerCase().includes(constant.toLowerCase());
|
4407
|
+
case 'is': return str.toLowerCase() === constant.toLowerCase();
|
4408
|
+
case '!is': return str.toLowerCase() !== constant.toLowerCase();
|
4409
|
+
case 'isEmpty': return str === '';
|
4410
|
+
case '!isEmpty': return str !== '';
|
4411
|
+
default: throw new Error('Invalid Filter. Operation does not match type');
|
4412
|
+
}
|
4413
|
+
}
|
4414
|
+
throw new Error('Invalid Filter. Unknown Type');
|
4415
|
+
}
|
4416
|
+
}
|
4417
|
+
class FilterBuilder {
|
4418
|
+
groups = [];
|
4419
|
+
constructor() {
|
4420
|
+
this.newGroup('WHERE');
|
4421
|
+
}
|
4422
|
+
static version() {
|
4423
|
+
return Filter.version();
|
4424
|
+
}
|
4425
|
+
static new() {
|
4426
|
+
return new FilterBuilder();
|
4427
|
+
}
|
4428
|
+
newGroup(logic) {
|
4429
|
+
if (this.groups.length !== 0 && logic === 'WHERE')
|
4430
|
+
throw new Error('Only the first groups can start with "WHERE"');
|
4431
|
+
this.groups.push({ logic: 'WHERE', conditions: [] });
|
4432
|
+
return this;
|
4433
|
+
}
|
4434
|
+
addCondition(logic, key, type, operation, constant) {
|
4435
|
+
const group = this.groups[this.groups.length - 1];
|
4436
|
+
if (group.conditions.length === 0 && logic !== 'WHERE')
|
4437
|
+
throw new Error('Logic for the first condition of a group must be "WHERE"');
|
4438
|
+
if (group.conditions.length > 0 && logic === 'WHERE')
|
4439
|
+
throw new Error('Only the first condition of a group can be "WHERE"');
|
4440
|
+
group.conditions.push({
|
4441
|
+
logic,
|
4442
|
+
key,
|
4443
|
+
type,
|
4444
|
+
operation,
|
4445
|
+
constant
|
4446
|
+
});
|
4447
|
+
return this;
|
4448
|
+
}
|
4449
|
+
finalise() {
|
4450
|
+
for (const group of this.groups) {
|
4451
|
+
if (group.conditions.length === 0)
|
4452
|
+
throw new Error('Bad Filter. All Groups must have atleast one condition');
|
4453
|
+
}
|
4454
|
+
return this.groups;
|
4455
|
+
}
|
4456
|
+
}
|
4457
|
+
|
4458
|
+
var index$1 = /*#__PURE__*/Object.freeze({
|
4459
|
+
__proto__: null,
|
4460
|
+
Trigger: Trigger,
|
4461
|
+
HandlerFunctionCallV2: HandlerFunctionCallV2,
|
4462
|
+
HandlerFunctionCall: HandlerFunctionCall,
|
4463
|
+
HandlerOnchainEvent: HandlerOnchainEvent
|
4464
|
+
});
|
4465
|
+
|
4466
|
+
var index = /*#__PURE__*/Object.freeze({
|
4467
|
+
__proto__: null,
|
4468
|
+
NOTIFY_DISCORD_WEBHOOK_RAW_ID: NOTIFY_DISCORD_WEBHOOK_RAW_ID,
|
4469
|
+
NotifyDiscordWebhook: NotifyDiscordWebhook,
|
4470
|
+
Notification: Notification
|
4471
|
+
});
|
4472
|
+
|
4473
|
+
exports.AlertBuilder = AlertBuilder;
|
4215
4474
|
exports.DeNotifyClient = DeNotifyClient;
|
4475
|
+
exports.FilterBuilder = FilterBuilder;
|
4476
|
+
exports.FunctionBuilder = FunctionBuilder;
|
4477
|
+
exports.Notification = index;
|
4478
|
+
exports.Trigger = index$1;
|