Skip to content
Snippets Groups Projects
Verified Commit b86b3da5 authored by Mateusz Tyszczak's avatar Mateusz Tyszczak :scroll:
Browse files

Add on alarm filter and provider

parent 29cc254a
No related branches found
No related tags found
1 merge request!25Add missing filters and providers
Showing
with 302 additions and 4 deletions
...@@ -29,6 +29,8 @@ export interface IAccount { ...@@ -29,6 +29,8 @@ export interface IAccount {
postingJsonMetadata: Record<string, any>; postingJsonMetadata: Record<string, any>;
jsonMetadata: Record<string, any>; jsonMetadata: Record<string, any>;
balance: IAccountBalance; balance: IAccountBalance;
recoveryAccount: string;
governanceVoteExpiration?: Date;
} }
export interface IAccountData { export interface IAccountData {
......
import { CollectorClassifierBase } from "./collector-classifier-base";
export interface IAccountChangingRecovery {
accountToRecover: string;
recoveryAccount: string;
effectiveOn: Date;
}
export interface IChangeRecoveryInProgressData {
recoveringAccounts: Record<string, IAccountChangingRecovery>;
}
export class ChangeRecoveryInProgressClassifier extends CollectorClassifierBase {
public type!: IChangeRecoveryInProgressData;
}
import { CollectorClassifierBase } from "./collector-classifier-base";
export interface IDeclinedVotingRightsAccount {
account: string;
effectiveDate: Date;
}
export interface IDeclineVotingRightsAccountsData {
declineVotingRightsAccounts: Record<string, IDeclinedVotingRightsAccount>;
}
export class DeclineVotingRightsClassifier extends CollectorClassifierBase {
public type!: IDeclineVotingRightsAccountsData;
}
...@@ -7,3 +7,5 @@ export { ImpactedAccountClassifier } from "./impacted-account-classifier"; ...@@ -7,3 +7,5 @@ export { ImpactedAccountClassifier } from "./impacted-account-classifier";
export { OperationClassifier } from "./operation-classifier"; export { OperationClassifier } from "./operation-classifier";
export { FeedPriceClassifier } from "./feed-price-classifier"; export { FeedPriceClassifier } from "./feed-price-classifier";
export { WitnessClassifier } from "./witness-classifier"; export { WitnessClassifier } from "./witness-classifier";
export { ChangeRecoveryInProgressClassifier } from "./change-recovery-in-progress-classifier";
export { DeclineVotingRightsClassifier } from "./decline-voting-rights-classifier";
...@@ -39,9 +39,16 @@ export class AccountCollector extends CollectorBase { ...@@ -39,9 +39,16 @@ export class AccountCollector extends CollectorBase {
const { accounts: apiAccounts } = await this.worker.chain!.api.database_api.find_accounts({ accounts: chunk }); const { accounts: apiAccounts } = await this.worker.chain!.api.database_api.find_accounts({ accounts: chunk });
for(const account of apiAccounts) for(const account of apiAccounts) {
let governanceVoteExpiration: Date | undefined = new Date(`${account.governance_vote_expiration_ts}Z`);
if (governanceVoteExpiration.getTime() <= 0) // Null time
governanceVoteExpiration = undefined;
accounts[account.name] = { accounts[account.name] = {
name: account.name, name: account.name,
recoveryAccount: account.recovery_account,
governanceVoteExpiration,
postingJsonMetadata: tryParseJson(account.posting_json_metadata), postingJsonMetadata: tryParseJson(account.posting_json_metadata),
jsonMetadata: tryParseJson(account.json_metadata), jsonMetadata: tryParseJson(account.json_metadata),
balance: { balance: {
...@@ -89,6 +96,7 @@ export class AccountCollector extends CollectorBase { ...@@ -89,6 +96,7 @@ export class AccountCollector extends CollectorBase {
} }
} }
}; };
}
} }
return { return {
......
import { IAccountChangingRecovery } from "../../classifiers/change-recovery-in-progress-classifier";
import { DataEvaluationContext } from "../../factories/data-evaluation-context";
import { CollectorBase, TAvailableClassifiers } from "../collector-base";
export interface IChangeRecoveryCollectorOptions {
changeRecoveryAccount: string;
}
const MAX_CHANGE_RECOVERY_GET_LIMIT = 100;
export class ChangeRecoveryInProgressCollector extends CollectorBase {
private readonly changeRecoveryAccounts: Record<string, number> = {};
protected pushOptions(data: IChangeRecoveryCollectorOptions): void {
this.changeRecoveryAccounts[data.changeRecoveryAccount] = (this.changeRecoveryAccounts[data.changeRecoveryAccount] || 0) + 1;
}
protected popOptions(data: IChangeRecoveryCollectorOptions): void {
this.changeRecoveryAccounts[data.changeRecoveryAccount] = (this.changeRecoveryAccounts[data.changeRecoveryAccount] || 1) - 1;
if (this.changeRecoveryAccounts[data.changeRecoveryAccount] === 0)
delete this.changeRecoveryAccounts[data.changeRecoveryAccount];
}
public async fetchData(_: DataEvaluationContext) {
const retrieveChangeRecoveryAccounts: Record<string, IAccountChangingRecovery> = {};
const recoveryAccounts = Object.keys(this.changeRecoveryAccounts);
for (let i = 0; i < recoveryAccounts.length; i += MAX_CHANGE_RECOVERY_GET_LIMIT) {
const chunk = recoveryAccounts.slice(i, i + MAX_CHANGE_RECOVERY_GET_LIMIT);
const { requests } = await this.worker.chain!.api.database_api.find_change_recovery_account_requests({ accounts: chunk });
for(const request of requests)
retrieveChangeRecoveryAccounts[request.account_to_recover] = {
accountToRecover: request.account_to_recover,
recoveryAccount: request.recovery_account,
effectiveOn: new Date(`${request.effective_on}Z`)
};
}
return {
ChangeRecoveryInProgressClassifier: {
recoveringAccounts: retrieveChangeRecoveryAccounts
}
} satisfies Partial<TAvailableClassifiers>;
};
}
import { IDeclinedVotingRightsAccount } from "../../classifiers/decline-voting-rights-classifier";
import { DataEvaluationContext } from "../../factories/data-evaluation-context";
import { CollectorBase, TAvailableClassifiers } from "../collector-base";
export interface IDeclineVotingRightsCollectorOptions {
declineVotingRightsAccount: string;
}
const MAX_DECLINED_VOTING_RIGHTS_GET_LIMIT = 100;
export class DeclineVotingRightsCollector extends CollectorBase {
private readonly declineVotingRightsAccounts: Record<string, number> = {};
protected pushOptions(data: IDeclineVotingRightsCollectorOptions): void {
this.declineVotingRightsAccounts[data.declineVotingRightsAccount] = (this.declineVotingRightsAccounts[data.declineVotingRightsAccount] || 0) + 1;
}
protected popOptions(data: IDeclineVotingRightsCollectorOptions): void {
this.declineVotingRightsAccounts[data.declineVotingRightsAccount] = (this.declineVotingRightsAccounts[data.declineVotingRightsAccount] || 1) - 1;
if (this.declineVotingRightsAccounts[data.declineVotingRightsAccount] === 0)
delete this.declineVotingRightsAccounts[data.declineVotingRightsAccount];
}
public async fetchData(_: DataEvaluationContext) {
const declineVotingRightsAccounts: Record<string, IDeclinedVotingRightsAccount> = {};
const recoveryAccounts = Object.keys(this.declineVotingRightsAccounts);
for (let i = 0; i < recoveryAccounts.length; i += MAX_DECLINED_VOTING_RIGHTS_GET_LIMIT) {
const chunk = recoveryAccounts.slice(i, i + MAX_DECLINED_VOTING_RIGHTS_GET_LIMIT);
const { requests } = await this.worker.chain!.api.database_api.find_decline_voting_rights_requests({ accounts: chunk });
for(const request of requests)
declineVotingRightsAccounts[request.account] = {
account: request.account,
effectiveDate: new Date(`${request.effective_date}Z`)
};
}
return {
DeclineVotingRightsClassifier: {
declineVotingRightsAccounts
}
} satisfies Partial<TAvailableClassifiers>;
};
}
...@@ -32,8 +32,8 @@ export class WitnessCollector extends CollectorBase { ...@@ -32,8 +32,8 @@ export class WitnessCollector extends CollectorBase {
const { witnesses: owners } = await this.worker.chain!.api.database_api.find_witnesses({ owners: chunk }); const { witnesses: owners } = await this.worker.chain!.api.database_api.find_witnesses({ owners: chunk });
for(const account of owners) for(const account of owners)
witnessNames[account.owner] = { witnesses[account.owner] = {
name: account.owner, owner: account.owner,
runningVersion: account.running_version, runningVersion: account.running_version,
totalMissedBlocks: account.total_missed, totalMissedBlocks: account.total_missed,
lastConfirmedBlockNum: account.last_confirmed_block_num lastConfirmedBlockNum: account.last_confirmed_block_num
......
import type { WorkerBee } from "../../../bot"; import type { WorkerBee } from "../../../bot";
import { import {
AccountClassifier, BlockClassifier, BlockHeaderClassifier, AccountClassifier, BlockClassifier, BlockHeaderClassifier,
ChangeRecoveryInProgressClassifier,
DeclineVotingRightsClassifier,
DynamicGlobalPropertiesClassifier, FeedPriceClassifier, ImpactedAccountClassifier, OperationClassifier, RcAccountClassifier, DynamicGlobalPropertiesClassifier, FeedPriceClassifier, ImpactedAccountClassifier, OperationClassifier, RcAccountClassifier,
WitnessClassifier WitnessClassifier
} from "../../classifiers"; } from "../../classifiers";
...@@ -11,6 +13,8 @@ import { ImpactedAccountCollector } from "../../collectors/common/impacted-accou ...@@ -11,6 +13,8 @@ import { ImpactedAccountCollector } from "../../collectors/common/impacted-accou
import { OperationCollector } from "../../collectors/common/operation-collector"; import { OperationCollector } from "../../collectors/common/operation-collector";
import { AccountCollector } from "../../collectors/jsonrpc/account-collector"; import { AccountCollector } from "../../collectors/jsonrpc/account-collector";
import { BlockCollector } from "../../collectors/jsonrpc/block-collector"; import { BlockCollector } from "../../collectors/jsonrpc/block-collector";
import { ChangeRecoveryInProgressCollector } from "../../collectors/jsonrpc/change-recovery-in-progress-collector";
import { DeclineVotingRightsCollector } from "../../collectors/jsonrpc/decline-voting-rights-collector";
import { DynamicGlobalPropertiesCollector } from "../../collectors/jsonrpc/dynamic-global-properties-collector"; import { DynamicGlobalPropertiesCollector } from "../../collectors/jsonrpc/dynamic-global-properties-collector";
import { FeedPriceCollector } from "../../collectors/jsonrpc/feed-price-collector"; import { FeedPriceCollector } from "../../collectors/jsonrpc/feed-price-collector";
import { RcAccountCollector } from "../../collectors/jsonrpc/rc-account-collector"; import { RcAccountCollector } from "../../collectors/jsonrpc/rc-account-collector";
...@@ -25,5 +29,7 @@ export const JsonRpcFactoryData: (worker: WorkerBee) => Array<[IEvaluationContex ...@@ -25,5 +29,7 @@ export const JsonRpcFactoryData: (worker: WorkerBee) => Array<[IEvaluationContex
[ImpactedAccountClassifier, new ImpactedAccountCollector(worker)], [ImpactedAccountClassifier, new ImpactedAccountCollector(worker)],
[OperationClassifier, new OperationCollector(worker)], [OperationClassifier, new OperationCollector(worker)],
[FeedPriceClassifier, new FeedPriceCollector(worker)], [FeedPriceClassifier, new FeedPriceCollector(worker)],
[WitnessClassifier, new WitnessCollector(worker)] [WitnessClassifier, new WitnessCollector(worker)],
[ChangeRecoveryInProgressClassifier, new ChangeRecoveryInProgressCollector(worker)],
[DeclineVotingRightsClassifier, new DeclineVotingRightsCollector(worker)]
]); ]);
import type { WorkerBee } from "../../bot";
import { AccountClassifier, ChangeRecoveryInProgressClassifier, DeclineVotingRightsClassifier } from "../classifiers";
import type { TRegisterEvaluationContext } from "../classifiers/collector-classifier-base";
import type { DataEvaluationContext } from "../factories/data-evaluation-context";
import { FilterBase } from "./filter-base";
export const STEEM_ACCOUNT_NAME = "steem";
export const ONE_MONTH_MS = 1000 * 60 * 60 * 24 * 31;
export class AlarmFilter extends FilterBase {
public constructor(
worker: WorkerBee,
private readonly account: string
) {
super(worker);
}
public usedContexts(): Array<TRegisterEvaluationContext> {
return [
AccountClassifier.forOptions({ account: this.account }),
ChangeRecoveryInProgressClassifier.forOptions({ changeRecoveryAccount: this.account }),
DeclineVotingRightsClassifier.forOptions({ declineVotingRightsAccount: this.account })
];
}
public async match(data: DataEvaluationContext): Promise<boolean> {
const { accounts } = await data.get(AccountClassifier);
if (accounts[this.account].recoveryAccount === STEEM_ACCOUNT_NAME)
return true;
if (accounts[this.account].governanceVoteExpiration === undefined)
return true;
if (accounts[this.account].governanceVoteExpiration!.getTime() < (Date.now() + ONE_MONTH_MS))
return true;
const { recoveringAccounts } = await data.get(ChangeRecoveryInProgressClassifier);
if (recoveringAccounts[this.account])
return true;
const { declineVotingRightsAccounts } = await data.get(DeclineVotingRightsClassifier);
if (declineVotingRightsAccounts[this.account])
return true;
return false;
}
}
import { TAccountName } from "@hiveio/wax";
import { AccountClassifier, ChangeRecoveryInProgressClassifier, DeclineVotingRightsClassifier } from "../classifiers";
import { TRegisterEvaluationContext } from "../classifiers/collector-classifier-base";
import { DataEvaluationContext } from "../factories/data-evaluation-context";
import { ONE_MONTH_MS, STEEM_ACCOUNT_NAME } from "../filters/alarm-filter";
import { ProviderBase } from "./provider-base";
export enum EAlarmType {
LEGACY_RECOVERY_ACCOUNT_SET,
GOVERNANCE_VOTE_EXPIRATION_SOON,
GOVERNANCE_VOTE_EXPIRED,
RECOVERY_ACCOUNT_IS_CHANGING,
DECLINING_VOTING_RIGHTS
}
export type TAlarmAccounts<TAccounts extends Array<TAccountName>> = {
[K in TAccounts[number]]: EAlarmType[];
};
export interface IAlarmAccountsData<TAccounts extends Array<TAccountName>> {
alarmsPerAccount: TAlarmAccounts<TAccounts>;
};
export class AlarmProvider<TAccounts extends Array<TAccountName> = Array<TAccountName>> extends ProviderBase {
public constructor(
private readonly accounts: TAccounts
) {
super();
}
public usedContexts(): Array<TRegisterEvaluationContext> {
return [
...this.accounts.map(account => AccountClassifier.forOptions({
account
})),
...this.accounts.map(account => ChangeRecoveryInProgressClassifier.forOptions({
changeRecoveryAccount: account
})),
...this.accounts.map(account => DeclineVotingRightsClassifier.forOptions({
declineVotingRightsAccount: account
}))
];
}
public async provide(data: DataEvaluationContext): Promise<IAlarmAccountsData<TAccounts>> {
const result: IAlarmAccountsData<TAccounts> = {
alarmsPerAccount: this.accounts.reduce((prev, curr) => {
prev[curr] = [];
return prev;
}, {} as TAlarmAccounts<TAccounts>)
};
const { accounts } = await data.get(AccountClassifier);
for(const account of this.accounts) {
if (accounts[account].recoveryAccount === STEEM_ACCOUNT_NAME)
result.alarmsPerAccount[account].push(EAlarmType.LEGACY_RECOVERY_ACCOUNT_SET);
if (accounts[account].governanceVoteExpiration === undefined)
result.alarmsPerAccount[account].push(EAlarmType.GOVERNANCE_VOTE_EXPIRED);
if (accounts[account].governanceVoteExpiration!.getTime() < (Date.now() + ONE_MONTH_MS))
result.alarmsPerAccount[account].push(EAlarmType.GOVERNANCE_VOTE_EXPIRATION_SOON);
}
const { recoveringAccounts } = await data.get(ChangeRecoveryInProgressClassifier);
for(const account of this.accounts)
if (recoveringAccounts[account])
result.alarmsPerAccount[account].push(EAlarmType.RECOVERY_ACCOUNT_IS_CHANGING);
const { declineVotingRightsAccounts } = await data.get(DeclineVotingRightsClassifier);
for(const account of this.accounts)
if (declineVotingRightsAccounts[account])
result.alarmsPerAccount[account].push(EAlarmType.DECLINING_VOTING_RIGHTS);
return result;
}
}
...@@ -4,6 +4,7 @@ import { WorkerBee } from "./bot"; ...@@ -4,6 +4,7 @@ import { WorkerBee } from "./bot";
import { AccountCreatedFilter } from "./chain-observers/filters/account-created-filter"; import { AccountCreatedFilter } from "./chain-observers/filters/account-created-filter";
import { AccountFullManabarFilter } from "./chain-observers/filters/account-full-manabar-filter"; import { AccountFullManabarFilter } from "./chain-observers/filters/account-full-manabar-filter";
import { AccountMetadataChangeFilter } from "./chain-observers/filters/account-metadata-change-filter"; import { AccountMetadataChangeFilter } from "./chain-observers/filters/account-metadata-change-filter";
import { AlarmFilter } from "./chain-observers/filters/alarm-filter";
import { BalanceChangeFilter } from "./chain-observers/filters/balance-change-filter"; import { BalanceChangeFilter } from "./chain-observers/filters/balance-change-filter";
import { BlockNumberFilter } from "./chain-observers/filters/block-filter"; import { BlockNumberFilter } from "./chain-observers/filters/block-filter";
import { LogicalAndFilter, LogicalOrFilter } from "./chain-observers/filters/composite-filter"; import { LogicalAndFilter, LogicalOrFilter } from "./chain-observers/filters/composite-filter";
...@@ -24,6 +25,7 @@ import { VoteFilter } from "./chain-observers/filters/vote-filter"; ...@@ -24,6 +25,7 @@ import { VoteFilter } from "./chain-observers/filters/vote-filter";
import { WhaleAlertFilter } from "./chain-observers/filters/whale-alert-filter"; import { WhaleAlertFilter } from "./chain-observers/filters/whale-alert-filter";
import { WitnessMissedBlocksFilter } from "./chain-observers/filters/witness-miss-block-filter"; import { WitnessMissedBlocksFilter } from "./chain-observers/filters/witness-miss-block-filter";
import { AccountProvider } from "./chain-observers/providers/account-provider"; import { AccountProvider } from "./chain-observers/providers/account-provider";
import { AlarmProvider } from "./chain-observers/providers/alarm-provider";
import { BlockHeaderProvider } from "./chain-observers/providers/block-header-provider"; import { BlockHeaderProvider } from "./chain-observers/providers/block-header-provider";
import { BlockProvider } from "./chain-observers/providers/block-provider"; import { BlockProvider } from "./chain-observers/providers/block-provider";
import { ExchangeTransferProvider } from "./chain-observers/providers/exchange-transfer-provider"; import { ExchangeTransferProvider } from "./chain-observers/providers/exchange-transfer-provider";
...@@ -180,6 +182,15 @@ export class QueenBee<TPreviousSubscriberData extends object = {}> { ...@@ -180,6 +182,15 @@ export class QueenBee<TPreviousSubscriberData extends object = {}> {
return this; return this;
} }
public onAlarm<
TAccount extends TAccountName
>(watchAccount: TAccount): QueenBee<TPreviousSubscriberData & Awaited<ReturnType<AlarmProvider<[TAccount]>["provide"]>>> {
this.operands.push(new AlarmFilter(this.worker, watchAccount));
this.providers.push(new AlarmProvider<[TAccount]>([watchAccount]));
return this;
}
public onImpactedAccount(account: TAccountName): QueenBee<TPreviousSubscriberData> { public onImpactedAccount(account: TAccountName): QueenBee<TPreviousSubscriberData> {
this.operands.push(new ImpactedAccountFilter(this.worker, account)); this.operands.push(new ImpactedAccountFilter(this.worker, account));
......
...@@ -22,6 +22,21 @@ export type WaxExtendTypes = { ...@@ -22,6 +22,21 @@ export type WaxExtendTypes = {
// ... // ...
}>; }; }>; };
}; };
find_decline_voting_rights_requests: {
params: { accounts: string[]; };
result: { requests: Array<{
account: string;
effective_date: string;
}>; };
};
find_change_recovery_account_requests: {
params: { accounts: string[]; };
result: { requests: Array<{
account_to_recover: string;
recovery_account: string;
effective_on: string;
}>; };
};
} }
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment