123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import TelegramBot from "node-telegram-bot-api";
- import { token, chatid, heartbeat, proposalDelay, wsLocation } from "../config";
- // types
- import { Block, Council, Options, Proposals } from "./types";
- import { types } from "@joystream/types";
- import { ApiPromise, WsProvider } from "@polkadot/api";
- import { AccountId, Header } from "@polkadot/types/interfaces";
- // functions
- import * as announce from "./lib/announcements";
- import * as get from "./lib/getters";
- import { parseArgs, printStatus, passedTime, exit } from "./lib/util";
- import moment from "moment";
- const opts: Options = parseArgs(process.argv.slice(2));
- const log = (msg: string): void | number => opts.verbose && console.log(msg);
- process.env.NTBA_FIX_319 ||
- log("TL;DR: Set NTBA_FIX_319 to hide this warning.");
- const bot = token ? new TelegramBot(token, { polling: true }) : null;
- let lastHeartbeat: number = moment().valueOf();
- const sendMessage = (msg: string) => {
- if (msg === "") return;
- try {
- if (bot) bot.sendMessage(chatid, msg, { parse_mode: "HTML" });
- else console.log(msg);
- } catch (e) {
- console.log(`Failed to send message: ${e}`);
- }
- };
- const main = async () => {
- const provider = new WsProvider(wsLocation);
- const api = await ApiPromise.create({ provider, types });
- await api.isReady;
- const [chain, node, version] = await Promise.all([
- String(await api.rpc.system.chain()),
- api.rpc.system.name(),
- api.rpc.system.version(),
- ]);
- let council: Council = { round: 0, last: "" };
- let blocks: Block[] = [];
- let lastEra = 0;
- let lastBlock: Block = {
- id: 0,
- duration: 6000,
- timestamp: lastHeartbeat,
- stake: 0,
- noms: 0,
- vals: 0,
- issued: 0,
- reward: 0,
- };
- let issued = 0;
- let reward = 0;
- let stake = 0;
- let vals = 0;
- let noms = 0;
- const channels: number[] = [0, 0];
- const posts: number[] = [0, 0];
- const threads: number[] = [0, 0];
- let proposals: Proposals = { last: 0, current: 0, active: [], executing: [] };
- let lastProposalUpdate = 0;
- if (opts.channel) channels[0] = await get.currentChannelId(api);
- if (opts.forum) {
- posts[0] = await get.currentPostId(api);
- threads[0] = await get.currentThreadId(api);
- }
- if (opts.proposals) {
- proposals.last = await get.proposalCount(api);
- proposals.active = await get.activeProposals(api, proposals.last);
- }
- const getReward = async (era: number) =>
- Number(await api.query.staking.erasValidatorReward(era));
- log(`Subscribed to ${chain} on ${node} v${version}`);
- api.rpc.chain.subscribeNewHeads(
- async (header: Header): Promise<void> => {
- // current block
- const id = header.number.toNumber();
- if (lastBlock.id === id) return;
- const timestamp = (await api.query.timestamp.now()).toNumber();
- const duration = timestamp - lastBlock.timestamp;
- // update validators and nominators every era
- const era = Number(await api.query.staking.currentEra());
- if (era > lastEra) {
- vals = (await api.query.session.validators()).length;
- stake = Number(await api.query.staking.erasTotalStake(era));
- issued = Number(await api.query.balances.totalIssuance());
- reward = (await getReward(era - 1)) || (await getReward(era - 2));
- // nominator count
- noms = 0;
- const nominators: { [key: string]: number } = {};
- const stashes = (await api.derive.staking.stashes())
- .map((s) => String(s))
- .map(async (v) => {
- const stakers = await api.query.staking.erasStakers(era, v);
- stakers.others.forEach(
- (n: { who: AccountId }) => nominators[String(n.who)]++
- );
- noms = Object.keys(nominators).length;
- });
- lastEra = era;
- }
- const block: Block = {
- id,
- timestamp,
- duration,
- stake,
- noms,
- vals,
- reward,
- issued,
- };
- blocks = blocks.concat(block);
- // heartbeat
- if (timestamp > lastHeartbeat + heartbeat) {
- const time = passedTime(lastHeartbeat, timestamp);
- blocks = announce.heartbeat(api, blocks, time, proposals, sendMessage);
- lastHeartbeat = block.timestamp;
- }
- // announcements
- if (opts.council && block.id > lastBlock.id)
- council = await announce.council(api, council, block.id, sendMessage);
- if (opts.channel) {
- channels[1] = await get.currentChannelId(api);
- if (channels[1] > channels[0])
- channels[0] = await announce.channels(api, channels, sendMessage);
- }
- if (opts.proposals) {
- proposals.current = await get.proposalCount(api);
- if (
- proposals.current > proposals.last ||
- (timestamp > lastProposalUpdate + 60000 * proposalDelay &&
- (proposals.active || proposals.executing))
- ) {
- proposals = await announce.proposals(api, proposals, id, sendMessage);
- lastProposalUpdate = timestamp;
- }
- }
- if (opts.forum) {
- posts[1] = await get.currentPostId(api);
- posts[0] = await announce.posts(api, posts, sendMessage);
- }
- printStatus(opts, { block: id, chain, posts, proposals });
- lastBlock = block
- }
- );
- };
- main().catch(() => exit(log));
|