balanceofsatoshis 11.52.1 → 11.52.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Versions
2
2
 
3
+ ## 11.52.2
4
+
5
+ - `telegram`: Fix connected node offline notifications
6
+
3
7
  ## 11.52.1
4
8
 
5
9
  - `fees`, `open`: Fix regression crash when setting fees on a pending channel
package/package.json CHANGED
@@ -37,14 +37,14 @@
37
37
  "ln-accounting": "5.0.5",
38
38
  "ln-service": "53.8.0",
39
39
  "ln-sync": "3.10.0",
40
- "ln-telegram": "3.16.0",
40
+ "ln-telegram": "3.17.1",
41
41
  "moment": "2.29.1",
42
42
  "paid-services": "3.11.0",
43
43
  "probing": "2.0.3",
44
44
  "psbt": "2.0.0",
45
45
  "qrcode-terminal": "0.12.0",
46
46
  "sanitize-filename": "1.6.3",
47
- "socks-proxy-agent": "6.1.1",
47
+ "socks-proxy-agent": "6.2.0-beta.0",
48
48
  "table": "6.8.0",
49
49
  "update-notifier": "5.1.0",
50
50
  "window-size": "1.1.1"
@@ -79,7 +79,7 @@
79
79
  "integration-tests": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 120 test/integration/*.js",
80
80
  "postpack": "PACKAGE_VERSION=$(cat package.json | grep \\\"version\\\" | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') && git tag -s v$PACKAGE_VERSION -m v$PACKAGE_VERSION && git push github --tags",
81
81
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
82
- "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/wallets/*.js"
82
+ "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
83
83
  },
84
- "version": "11.52.1"
84
+ "version": "11.52.2"
85
85
  }
@@ -1,16 +1,15 @@
1
- const {homedir} = require('os');
2
- const {join} = require('path');
3
-
4
1
  const asyncAuto = require('async/auto');
5
2
  const asyncForever = require('async/forever');
6
3
  const asyncMap = require('async/map');
7
- const {getWalletVersion} = require('ln-service');
4
+ const {getWalletInfo} = require('ln-service');
5
+ const {postNodesOffline} = require('ln-telegram');
8
6
  const {returnResult} = require('asyncjs-util');
9
7
 
10
8
  const {getLnds} = require('./../lnd');
11
- const startTelegramBot = require('./start_telegram_bot');
9
+ const getTelegramBot = require('./get_telegram_bot');
10
+ const runTelegramBot = require('./run_telegram_bot');
12
11
 
13
- const home = '.bos';
12
+ const defaultPaymentsBudget = 0;
14
13
  const restartDelayMs = 1000 * 60 * 3;
15
14
 
16
15
  /** Connect nodes to Telegram
@@ -63,68 +62,72 @@ module.exports = (args, cbk) => {
63
62
  return cbk();
64
63
  },
65
64
 
66
- // Check nodes
67
- checkNodes: ['validate', async () => {
65
+ // Get the nodes
66
+ getNodes: ['validate', async () => {
68
67
  const {nodes} = args;
69
68
 
70
69
  const {lnds} = await getLnds({nodes, logger: args.logger});
71
70
 
72
71
  const withName = lnds.map((lnd, i) => ({lnd, node: (nodes || [])[i]}));
73
72
 
74
- return await asyncMap(withName, async ({lnd, node}) => {
73
+ return asyncMap(withName, async ({lnd, node}) => {
75
74
  try {
76
- return await getWalletVersion({lnd});
75
+ const wallet = await getWalletInfo({lnd});
76
+
77
+ return {node, alias: wallet.alias, id: wallet.public_key};
77
78
  } catch (err) {
78
79
  args.logger.error({node, err: 'failed_to_connect'});
79
- }
80
80
 
81
- return;
81
+ throw err;
82
+ }
82
83
  });
83
84
  }],
84
85
 
85
- // Home directory path
86
- path: ['checkNodes', ({}, cbk) => {
87
- return cbk(null, join(...[homedir(), home]));
86
+ // Get the telegram bot
87
+ getBot: ['validate', ({}, cbk) => {
88
+ return getTelegramBot({fs: args.fs, proxy: args.proxy}, cbk);
88
89
  }],
89
90
 
90
91
  // Start bot
91
- startTelegram: ['checkNodes', ({}, cbk) => {
92
+ start: ['getBot', 'getNodes', ({getBot, getNodes}, cbk) => {
92
93
  let {limit} = args.payments;
94
+ let online = getNodes.map(n => n.id);
93
95
 
94
96
  return asyncForever(cbk => {
95
- return getLnds({
97
+ return runTelegramBot({
98
+ bot: getBot.bot,
99
+ fs: args.fs,
100
+ id: args.id,
101
+ min_forward_tokens: args.min_forward_tokens,
96
102
  logger: args.logger,
97
- nodes: args.nodes
103
+ nodes: args.nodes,
104
+ payments_limit: limit || defaultPaymentsBudget,
105
+ request: args.request,
98
106
  },
99
107
  (err, res) => {
100
- // Exit the loop when the backing LNDs cannot be found
101
108
  if (!!err) {
102
109
  return cbk(err);
103
110
  }
104
111
 
105
- const {lnds} = res;
106
-
107
- args.logger.info({connecting_to_telegram: args.nodes});
108
-
109
- return startTelegramBot({
110
- lnds,
111
- fs: args.fs,
112
- id: args.id,
113
- limits: {
114
- min_forward_tokens: args.min_forward_tokens || undefined,
115
- },
116
- logger: args.logger,
117
- payments: {limit},
118
- proxy: args.proxy,
119
- request: args.request,
112
+ const offline = online.filter(id => !res.online.includes(id));
113
+
114
+ // Refresh the current online status
115
+ online = res.online.slice();
116
+
117
+ // Reset payment budget
118
+ limit = Number();
119
+
120
+ return postNodesOffline({
121
+ bot: getBot.bot,
122
+ connected: res.connected,
123
+ offline: getNodes.filter(n => offline.includes(n.id)),
120
124
  },
121
125
  err => {
122
- args.logger.error(err || [503, 'TelegramBotFailed']);
123
-
124
- // Reset payment budget
125
- limit = Number();
126
+ if (!!err) {
127
+ return cbk(err);
128
+ }
126
129
 
127
- return setTimeout(() => cbk(), restartDelayMs);
130
+ return setTimeout(cbk, restartDelayMs);
128
131
  });
129
132
  });
130
133
  },
@@ -0,0 +1,78 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+ const {SocksProxyAgent} = require('socks-proxy-agent');
4
+
5
+ const {parse} = JSON;
6
+
7
+ /** Get a SOCKS proxy given a JSON configuration file path
8
+
9
+ {
10
+ fs: {
11
+ getFile: <Get File From Filesystem Function>
12
+ }
13
+ path: <Path To Proxy Configuration JSON File String>
14
+ }
15
+
16
+ @returns via cbk or Promise
17
+ {
18
+ agent: <Socks Proxy Agent Object>
19
+ }
20
+ */
21
+ module.exports = ({fs, path}, cbk) => {
22
+ return new Promise((resolve, reject) => {
23
+ return asyncAuto({
24
+ // Check arguments
25
+ validate: cbk => {
26
+ if (!fs) {
27
+ return cbk([400, 'ExpectedFileSystemMethodsToGetSocksProxyAgent']);
28
+ }
29
+
30
+ if (!path) {
31
+ return cbk([400, 'ExpectedPathToSocksJsonFileToGetSocksProxyAgent']);
32
+ }
33
+
34
+ return cbk();
35
+ },
36
+
37
+ // Get the configuration file
38
+ getFile: ['validate', ({}, cbk) => {
39
+ return fs.getFile(path, (err, res) => {
40
+ if (!!err) {
41
+ return cbk([400, 'FailedToFindFileAtProxySpecifiedPath', {err}]);
42
+ }
43
+
44
+ if (!res) {
45
+ return cbk([400, 'ExpectedFileDataAtProxySpecifiedPath']);
46
+ }
47
+
48
+ return cbk(null, res.toString());
49
+ });
50
+ }],
51
+
52
+ // Construct the SOCKS proxy agent
53
+ agent: ['getFile', ({getFile}, cbk) => {
54
+ try {
55
+ parse(getFile);
56
+ } catch (err) {
57
+ return cbk([400, 'ExpectedValidJsonConfigFileForProxy', {err}]);
58
+ }
59
+
60
+ const {host, password, port, userId} = parse(getFile);
61
+
62
+ try {
63
+ const agent = new SocksProxyAgent({
64
+ password,
65
+ port,
66
+ userId,
67
+ hostname: host,
68
+ });
69
+
70
+ return cbk(null, {agent});
71
+ } catch (err) {
72
+ return cbk([503, 'FailedToCreateSocksProxyAgent', {err}]);
73
+ }
74
+ }],
75
+ },
76
+ returnResult({reject, resolve, of: 'agent'}, cbk));
77
+ });
78
+ };
@@ -0,0 +1,122 @@
1
+ const {homedir} = require('os');
2
+ const {join} = require('path');
3
+
4
+ const asyncAuto = require('async/auto');
5
+ const asyncReflect = require('async/reflect');
6
+ const {Bot} = require('grammy');
7
+ const inquirer = require('inquirer');
8
+ const {returnResult} = require('asyncjs-util');
9
+
10
+ const getSocksProxy = require('./get_socks_proxy');
11
+ const interaction = require('./interaction');
12
+
13
+ const botKeyFile = 'telegram_bot_api_key';
14
+ const home = '.bos';
15
+
16
+ /** Get the Telegram Bot object
17
+
18
+ {
19
+ fs: {
20
+ getFile: <Get File Contents Function>
21
+ getFileStatus: <Get File Status Function>
22
+ makeDirectory: <Make Directory Function>
23
+ writeFile: <Write File Function>
24
+ }
25
+ [proxy]: <Proxy Details JSON File Path String>
26
+ }
27
+
28
+ @returns via cbk or Promise
29
+ {
30
+ bot: <Telegram Bot Object>
31
+ key: <Telegram API Key String>
32
+ }
33
+ */
34
+ module.exports = ({fs, proxy}, cbk) => {
35
+ return new Promise((resolve, reject) => {
36
+ return asyncAuto({
37
+ // Check arguments
38
+ validate: cbk => {
39
+ if (!fs) {
40
+ return cbk([400, 'ExpectedFileSystemMethodsToGetTelegramBot']);
41
+ }
42
+
43
+ return cbk();
44
+ },
45
+
46
+ // Get proxy agent
47
+ getProxy: ['validate', ({}, cbk) => {
48
+ // Exit early if not using a proxy
49
+ if (!proxy) {
50
+ return cbk();
51
+ }
52
+
53
+ return getSocksProxy({fs, path: proxy}, cbk);
54
+ }],
55
+
56
+ // Home directory path
57
+ path: ['validate', ({}, cbk) => cbk(null, join(...[homedir(), home]))],
58
+
59
+ // Ask for an API key
60
+ apiKey: ['path', ({path}, cbk) => {
61
+ return fs.getFile(join(...[path, botKeyFile]), (err, res) => {
62
+ // Exit early when resetting the API key
63
+ if (!!err || !res || !res.toString() || !!fs.is_reset_state) {
64
+ const token = interaction.api_token_prompt;
65
+
66
+ inquirer.prompt([token]).then(({key}) => cbk(null, {key}));
67
+
68
+ return;
69
+ }
70
+
71
+ return cbk(null, {is_saved: true, key: res.toString()});
72
+ });
73
+ }],
74
+
75
+ // Create the bot
76
+ createBot: ['apiKey', 'getProxy', ({apiKey, getProxy}, cbk) => {
77
+ const {key} = apiKey;
78
+
79
+ // Exit early when there is no SOCKS proxy
80
+ if (!getProxy) {
81
+ return cbk(null, {key, bot: new Bot(key)});
82
+ }
83
+
84
+ // Initiate bot using proxy agent when configured
85
+ const bot = new Bot(key, {
86
+ client: {baseFetchConfig: {agent: getProxy.agent, compress: true}},
87
+ });
88
+
89
+ return cbk(null, {bot, key});
90
+ }],
91
+
92
+ // Test the created bot
93
+ test: ['createBot', async ({createBot}) => {
94
+ // Start the bot
95
+ return await createBot.bot.init();
96
+ }],
97
+
98
+ // Make the home directory if it's not already present
99
+ makeDir: ['apiKey', 'path', 'test', asyncReflect(({path}, cbk) => {
100
+ return fs.makeDirectory(path, cbk);
101
+ })],
102
+
103
+ // Save the bot API key so it doesn't need to be entered next run
104
+ saveKey: ['apiKey', 'path', 'makeDir', ({apiKey, path}, cbk) => {
105
+ // Exit early when API key is already saved
106
+ if (!!apiKey.is_saved) {
107
+ return cbk();
108
+ }
109
+
110
+ // Ignore errors when making directory, it may already be present
111
+ return fs.writeFile(join(...[path, botKeyFile]), apiKey.key, err => {
112
+ if (!!err) {
113
+ return cbk([503, 'FailedToSaveTelegramApiToken', {err}]);
114
+ }
115
+
116
+ return cbk();
117
+ });
118
+ }],
119
+ },
120
+ returnResult({reject, resolve, of: 'createBot'}, cbk));
121
+ });
122
+ };
@@ -0,0 +1,128 @@
1
+ const asyncAuto = require('async/auto');
2
+ const asyncFilter = require('async/filter');
3
+ const asyncMap = require('async/map');
4
+ const {getIdentity} = require('ln-service');
5
+ const {returnResult} = require('asyncjs-util');
6
+
7
+ const {getLnds} = require('./../lnd');
8
+ const startTelegramBot = require('./start_telegram_bot');
9
+
10
+ const defaultError = [503, 'TelegramBotStopped'];
11
+ const {isArray} = Array;
12
+
13
+ /** Run the telegram bot for a node or multiple nodes
14
+
15
+ {
16
+ bot: <Telegram Bot Object>
17
+ fs: {
18
+ getFile: <Get File Contents Function>
19
+ [is_reset_state]: <Reset File Status Bool>
20
+ makeDirectory: <Make Directory Function>
21
+ writeFile: <Write File Function>
22
+ }
23
+ [id]: <Authorized User Id Number>
24
+ [min_forward_tokens]: <Minimum Forward Tokens To Notify Number>
25
+ logger: <Winston Logger Object>
26
+ nodes: [<Node Name String>]
27
+ payments_limit: <Total Spendable Budget Tokens Limit Number>
28
+ [proxy]: <Socks Proxy Agent Object>
29
+ request: <Request Function>
30
+ }
31
+
32
+ @returns via cbk or Promise
33
+ {
34
+ [connected]: <Connected Id Number>
35
+ online: [{
36
+ alias: <Node Alias String>
37
+ id: <Node Public Key Id Hex String>
38
+ }]
39
+ }
40
+ */
41
+ module.exports = (args, cbk) => {
42
+ return new Promise((resolve, reject) => {
43
+ return asyncAuto({
44
+ // Check arguments
45
+ validate: cbk => {
46
+ if (!args.bot) {
47
+ return cbk([400, 'ExpectedTelegramBotToRunTelegramBot']);
48
+ }
49
+
50
+ if (!args.fs) {
51
+ return cbk([400, 'ExpectedFilesystemMethodsToRunTelegramBot']);
52
+ }
53
+
54
+ if (!args.logger) {
55
+ return cbk([400, 'ExpectedWinstonLoggerToRunTelegramBot']);
56
+ }
57
+
58
+ if (!isArray(args.nodes)) {
59
+ return cbk([400, 'ExpectedArrayOfSavedNodesToRunTelegramBot']);
60
+ }
61
+
62
+ if (args.payments_limit === undefined) {
63
+ return cbk([400, 'ExpectedPaymentsLimitToRunTelegramBot']);
64
+ }
65
+
66
+ if (!args.request) {
67
+ return cbk([400, 'ExpectedRequestFunctionToRunTelegrambot']);
68
+ }
69
+
70
+ return cbk();
71
+ },
72
+
73
+ // Get associated LNDs
74
+ getLnds: ['validate', ({}, cbk) => {
75
+ return getLnds({logger: args.logger, nodes: args.nodes}, cbk);
76
+ }],
77
+
78
+ // Start the bot going
79
+ startBot: ['getLnds', ({getLnds}, cbk) => {
80
+ args.logger.info({connecting_to_telegram: args.nodes});
81
+
82
+ return startTelegramBot({
83
+ bot: args.bot,
84
+ fs: args.fs,
85
+ id: args.id,
86
+ min_forward_tokens: args.min_forward_tokens,
87
+ lnds: getLnds.lnds,
88
+ logger: args.logger,
89
+ payments_limit: args.payments_limit,
90
+ proxy: args.proxy,
91
+ request: args.request,
92
+ },
93
+ cbk);
94
+ }],
95
+
96
+ // Check the LNDs that they can connect
97
+ getConnected: ['getLnds', 'startBot', ({getLnds}, cbk) => {
98
+ return asyncMap(getLnds.lnds, (lnd, cbk) => {
99
+ return getIdentity({lnd}, (err, res) => {
100
+ // Return no id when there is an error getting the wallet info
101
+ if (!!err) {
102
+ return cbk();
103
+ }
104
+
105
+ return cbk(null, res.public_key);
106
+ });
107
+ },
108
+ cbk);
109
+ }],
110
+
111
+ // Final set of connected nodes
112
+ online: ['getConnected', 'startBot', ({getConnected, startBot}, cbk) => {
113
+ // Report the failure that killed the bot
114
+ if (!!startBot.failure) {
115
+ args.logger.error({err: startBot.failure});
116
+ }
117
+
118
+ const online = getConnected.filter(n => !!n);
119
+
120
+ return cbk(null, {
121
+ connected: startBot.connected,
122
+ online: getConnected.filter(n => !!n),
123
+ });
124
+ }],
125
+ },
126
+ returnResult({reject, resolve, of: 'online'}, cbk));
127
+ });
128
+ };