balanceofsatoshis 11.34.0 → 11.36.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,13 @@
1
1
  # Versions
2
2
 
3
+ ## 11.36.2
4
+
5
+ - `telegram`: Add support for `--use-proxy` to specify a SOCKS proxy server
6
+
7
+ ## 11.35.0
8
+
9
+ - `graph`: Add `HOPS` variable to `--filter` for node peer distance from self
10
+
3
11
  ## 11.34.0
4
12
 
5
13
  - `telegram`: Allow switching the node of a trade-secret
package/bos CHANGED
@@ -761,6 +761,8 @@ prog
761
761
  // Get the edges of a given node
762
762
  .command('graph', 'List out the connections a node has with other nodes')
763
763
  .argument('<alias_or_public_key>', 'Node in the graph to look up')
764
+ .help('--filter variables: AGE/CAPACITY/HOPS/IN_FEE_RATE/OUT_FEE_RATE')
765
+ .help('Example: --filter "age<7*144" for connections in the last week')
764
766
  .option('--filter <formula>', 'Filter formula to apply', REPEATABLE)
765
767
  .option('--node <node_name>', 'Node to use for lookup')
766
768
  .option('--sort <sort_connections_by', 'Sort peers by field')
@@ -1605,11 +1607,13 @@ prog
1605
1607
  .help('Supported updates: forwards, received payments, etc')
1606
1608
  .help('Multiple nodes are supported by repeating the `--node` flag')
1607
1609
  .help('See README for info on persisting the bot through Docker/nohup')
1610
+ .help('--use-proxy requires path to JSON file for host/password/port/userId')
1608
1611
  .option('--budget <amount>', 'Spending amount to allow', INT, Number())
1609
1612
  .option('--connect <connect_code>', 'Connection code', INT)
1610
1613
  .option('--ignore-forwards-below <amount>', 'Ignore forwards of value', INT)
1611
1614
  .option('--node <node_name>', 'Node to connect to Telegram', REPEATABLE)
1612
1615
  .option('--reset-api-key', 'Reset the Telegram API key')
1616
+ .option('--use-proxy <path>', 'Proxy agent to connect to Telegram')
1613
1617
  .action((args, options, logger) => {
1614
1618
  return new Promise(async (resolve, reject) => {
1615
1619
  try {
@@ -1626,6 +1630,7 @@ prog
1626
1630
  min_forward_tokens: options.ignoreForwardsBelow || undefined,
1627
1631
  nodes: flatten([options.node].filter(n => !!n)),
1628
1632
  payments: {limit: options.budget},
1633
+ proxy: options.useProxy || undefined,
1629
1634
  request: commands.fetchRequest({fetch}),
1630
1635
  });
1631
1636
  } catch (err) {
@@ -9,6 +9,7 @@ const {getHeight} = require('ln-service');
9
9
  const {getNetworkGraph} = require('ln-service');
10
10
  const {getNode} = require('ln-service');
11
11
  const {getNodeAlias} = require('ln-sync');
12
+ const {getRouteToDestination} = require('ln-service');
12
13
  const moment = require('moment');
13
14
  const {returnResult} = require('asyncjs-util');
14
15
 
@@ -24,7 +25,10 @@ const defaultSort = 'age';
24
25
  const disableTag = isDisabled => isDisabled ? `${emojiIcons.disabled} ` : '';
25
26
  const displayFee = (n, rate) => n.length ? formatFeeRate({rate}).display : ' ';
26
27
  const displayTokens = tokens => formatTokens({tokens}).display;
28
+ const distanceTokens = 100;
29
+ const hasDistanceFilter = filters => /hops/gim.test(filters.join(' '));
27
30
  const header = [['Alias','Age','In Fee','Capacity','Out Fee','Public Key']];
31
+ const hopsTitle = 'Hops';
28
32
  const {isArray} = Array;
29
33
  const isClear = sockets => !!sockets.find(n => !!isIP(n.socket.split(':')[0]));
30
34
  const isLarge = features => !!features.find(n => n.type === 'large_channels');
@@ -144,17 +148,49 @@ module.exports = ({filters, fs, lnd, logger, query, sort}, cbk) => {
144
148
  return cbk();
145
149
  }],
146
150
 
151
+ // Get distances
152
+ getDistances: ['peerKeys', ({peerKeys}, cbk) => {
153
+ // Exit early when there is no distance filter
154
+ if (!hasDistanceFilter(filters)) {
155
+ return cbk(null, peerKeys.map(destination => ({destination})));
156
+ }
157
+
158
+ return asyncMap(peerKeys, (destination, cbk) => {
159
+ return getRouteToDestination({
160
+ destination,
161
+ lnd,
162
+ is_ignoring_past_failure: true,
163
+ tokens: distanceTokens,
164
+ },
165
+ (err, res) => {
166
+ if (!!err) {
167
+ return cbk(err);
168
+ }
169
+
170
+ if (!res.route) {
171
+ return cbk(null, {destination, hops: Infinity});
172
+ }
173
+
174
+ return cbk(null, {destination, hops: --res.route.hops.length});
175
+ });
176
+ },
177
+ cbk);
178
+ }],
179
+
147
180
  // Final set of peers
148
181
  peers: [
182
+ 'getDistances',
149
183
  'getHeight',
150
184
  'getIcons',
151
185
  'getNode',
152
186
  'peerKeys',
153
- ({getHeight, getIcons, getNode, peerKeys}, cbk) =>
187
+ ({getDistances, getHeight, getIcons, getNode, peerKeys}, cbk) =>
154
188
  {
155
189
  const sorting = sort || defaultSort;
156
190
 
157
191
  const peers = peerKeys.map(peerKey => {
192
+ const {hops} = getDistances.find(n => n.destination === peerKey);
193
+
158
194
  const capacity = getNode.channels
159
195
  .filter(n => !!n.policies.find(n => n.public_key === peerKey))
160
196
  .reduce((sum, {capacity}) => sum + capacity, Number());
@@ -192,6 +228,7 @@ module.exports = ({filters, fs, lnd, logger, query, sort}, cbk) => {
192
228
  filters,
193
229
  variables: {
194
230
  capacity,
231
+ hops,
195
232
  age: getHeight.current_block_height - connectHeight,
196
233
  height: connectHeight,
197
234
  in_fee_rate: inboundFeeRate,
@@ -217,6 +254,10 @@ module.exports = ({filters, fs, lnd, logger, query, sort}, cbk) => {
217
254
  peerKey,
218
255
  ];
219
256
 
257
+ if (!!hasDistanceFilter(filters)) {
258
+ row.unshift(hops);
259
+ }
260
+
220
261
  return {sorts, row};
221
262
  })
222
263
  .filter(n => !!n);
@@ -258,7 +299,20 @@ module.exports = ({filters, fs, lnd, logger, query, sort}, cbk) => {
258
299
 
259
300
  // Final set of rows
260
301
  rows: ['getRowsWithAliases', ({getRowsWithAliases}, cbk) => {
261
- return cbk(null, {rows: [].concat(header).concat(getRowsWithAliases)});
302
+ const [titles] = header;
303
+
304
+ const headers = titles.slice();
305
+
306
+ if (!!hasDistanceFilter(filters)) {
307
+ const first = headers.shift();
308
+
309
+ headers.unshift(hopsTitle);
310
+ headers.unshift(first);
311
+ }
312
+
313
+ const rows = [].concat([headers]).concat(getRowsWithAliases);
314
+
315
+ return cbk(null, {rows});
262
316
  }],
263
317
  },
264
318
  returnResult({reject, resolve, of: 'rows'}, cbk));
package/package.json CHANGED
@@ -37,13 +37,14 @@
37
37
  "ln-accounting": "5.0.5",
38
38
  "ln-service": "53.5.2",
39
39
  "ln-sync": "3.7.0",
40
- "ln-telegram": "3.10.0",
40
+ "ln-telegram": "3.11.0",
41
41
  "moment": "2.29.1",
42
42
  "paid-services": "3.11.0",
43
43
  "probing": "2.0.2",
44
- "psbt": "1.1.10",
44
+ "psbt": "1.1.11",
45
45
  "qrcode-terminal": "0.12.0",
46
46
  "sanitize-filename": "1.6.3",
47
+ "socks-proxy-agent": "6.1.1",
47
48
  "table": "6.8.0",
48
49
  "update-notifier": "5.1.0",
49
50
  "window-size": "1.1.1"
@@ -80,5 +81,5 @@
80
81
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
81
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
83
  },
83
- "version": "11.34.0"
84
+ "version": "11.36.2"
84
85
  }
@@ -29,6 +29,7 @@ const restartDelayMs = 1000 * 60 * 3;
29
29
  payments: {
30
30
  [limit]: <Total Spendable Budget Tokens Limit Number>
31
31
  }
32
+ [proxy]: <Path to Proxy JSON File String>
32
33
  request: <Request Function>
33
34
  }
34
35
 
@@ -114,6 +115,7 @@ module.exports = (args, cbk) => {
114
115
  },
115
116
  logger: args.logger,
116
117
  payments: {limit},
118
+ proxy: args.proxy,
117
119
  request: args.request,
118
120
  },
119
121
  err => {
@@ -18,6 +18,7 @@ const {handleButtonPush} = require('ln-telegram');
18
18
  const {handleConnectCommand} = require('ln-telegram');
19
19
  const {handleCostsCommand} = require('ln-telegram');
20
20
  const {handleEarningsCommand} = require('ln-telegram');
21
+ const {handleEditedMessage} = require('ln-telegram');
21
22
  const {handleInvoiceCommand} = require('ln-telegram');
22
23
  const {handleLiquidityCommand} = require('ln-telegram');
23
24
  const {handleMempoolCommand} = require('ln-telegram');
@@ -39,6 +40,7 @@ const {postUpdatedBackup} = require('ln-telegram');
39
40
  const {returnResult} = require('asyncjs-util');
40
41
  const {sendMessage} = require('ln-telegram');
41
42
  const {serviceAnchoredTrades} = require('paid-services');
43
+ const SocksProxyAgent = require('socks-proxy-agent');
42
44
  const {subscribeToBackups} = require('ln-service');
43
45
  const {subscribeToBlocks} = require('goldengate');
44
46
  const {subscribeToChannels} = require('ln-service');
@@ -47,7 +49,6 @@ const {subscribeToPastPayments} = require('ln-service');
47
49
  const {subscribeToTransactions} = require('ln-service');
48
50
 
49
51
  const interaction = require('./interaction');
50
- const markdown = {parse_mode: 'Markdown'};
51
52
  const named = require('./../package').name;
52
53
  const {version} = require('./../package');
53
54
 
@@ -61,9 +62,11 @@ const home = '.bos';
61
62
  const {isArray} = Array;
62
63
  const isNumber = n => !isNaN(n);
63
64
  const limit = 99999;
65
+ const markdown = {parse_mode: 'Markdown'};
64
66
  const maxCommandDelayMs = 1000 * 10;
65
67
  const msSince = epoch => Date.now() - (epoch * 1e3);
66
68
  const network = 'btc';
69
+ const {parse} = JSON;
67
70
  const restartSubscriptionTimeMs = 1000 * 30;
68
71
  const sanitize = n => (n || '').replace(/_/g, '\\_').replace(/[*~`]/g, '');
69
72
 
@@ -85,12 +88,15 @@ const sanitize = n => (n || '').replace(/_/g, '\\_').replace(/[*~`]/g, '');
85
88
  payments: {
86
89
  [limit]: <Total Spendable Budget Tokens Limit Number>
87
90
  }
91
+ [proxy]: <Path to Proxy JSON File String>
88
92
  request: <Request Function>
89
93
  }
90
94
 
91
95
  @returns via cbk or Promise
92
96
  */
93
- module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
97
+ module.exports = (args, cbk) => {
98
+ const {fs, id, limits, lnds, logger, payments, request} = args;
99
+
94
100
  let connectedId = id;
95
101
  let isStopped = false;
96
102
  let paymentsLimit = !payments || !payments.limit ? Number() : payments.limit;
@@ -169,6 +175,45 @@ module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
169
175
  cbk);
170
176
  }],
171
177
 
178
+ // Get proxy agent
179
+ getProxyAgent: ['validate', ({}, cbk) => {
180
+ // Exit early if not using a proxy
181
+ if (!args.proxy) {
182
+ return cbk();
183
+ }
184
+
185
+ return args.fs.getFile(args.proxy, (err, res) => {
186
+ if (!!err) {
187
+ return cbk([503, 'FailedToFindFileAtProxySpecifiedPath', {err}]);
188
+ }
189
+
190
+ if (!res) {
191
+ return cbk([503, 'ExpectedFileDataAtProxySpecifiedPath']);
192
+ }
193
+
194
+ try {
195
+ parse(res.toString());
196
+ } catch (err) {
197
+ return cbk([503, 'ExpectedValidJsonConfigFileForProxy']);
198
+ }
199
+
200
+ const {host, password, port, userId} = parse(res);
201
+
202
+ try {
203
+ const socksAgent = new SocksProxyAgent({
204
+ host,
205
+ password,
206
+ port,
207
+ userId,
208
+ });
209
+
210
+ return cbk(null, socksAgent);
211
+ } catch (err) {
212
+ return cbk([503, 'FailedToCreateSocksProxyAgent', {err}]);
213
+ }
214
+ });
215
+ }],
216
+
172
217
  // Save API key
173
218
  saveKey: ['apiKey', ({apiKey}, cbk) => {
174
219
  // Exit early when API key is already saved
@@ -192,7 +237,12 @@ module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
192
237
  }],
193
238
 
194
239
  // Setup the bot start action
195
- initBot: ['apiKey', 'getNodes', ({apiKey, getNodes}, cbk) => {
240
+ initBot: [
241
+ 'apiKey',
242
+ 'getNodes',
243
+ 'getProxyAgent',
244
+ ({apiKey, getNodes, getProxyAgent}, cbk) =>
245
+ {
196
246
  allNodes = getNodes;
197
247
 
198
248
  // Exit early when bot is already instantiated
@@ -200,7 +250,14 @@ module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
200
250
  return cbk();
201
251
  }
202
252
 
203
- bot = new Bot(apiKey.key);
253
+ // Initiate bot using proxy agent when configured
254
+ if (!!getProxyAgent) {
255
+ bot = new Bot(apiKey.key, {
256
+ client: {baseFetchConfig: {agent: getProxyAgent, compress: true}},
257
+ });
258
+ } else {
259
+ bot = new Bot(apiKey.key);
260
+ }
204
261
 
205
262
  bot.api.setMyCommands([
206
263
  {command: 'backup', description: 'Get node backup file'},
@@ -219,18 +276,11 @@ module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
219
276
 
220
277
  bot.catch(err => logger.error({telegram_error: err}));
221
278
 
222
- bot.use((ctx, next) => {
223
- // Ignore messages that are old
224
- if (!!ctx.message && msSince(ctx.message.date) > maxCommandDelayMs) {
225
- return;
226
- }
227
-
228
- // Warn on edit of old message
229
- if (!!ctx.update && !!ctx.update.edited_message) {
230
- const {text} = ctx.update.edited_message;
231
- const warning = interaction.edit_message_warning;
232
-
233
- return ctx.reply(`${warning}\n${text}`, markdown);
279
+ bot.use(async (ctx, next) => {
280
+ try {
281
+ await handleEditedMessage({ctx});
282
+ } catch (err) {
283
+ logger.error({err});
234
284
  }
235
285
 
236
286
  return next();
@@ -239,7 +289,6 @@ module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
239
289
  bot.command('backup', ctx => {
240
290
  handleBackupCommand({
241
291
  logger,
242
- request,
243
292
  from: ctx.message.from.id,
244
293
  id: connectedId,
245
294
  key: apiKey.key,
@@ -337,12 +386,10 @@ module.exports = ({fs, id, limits, lnds, logger, payments, request}, cbk) => {
337
386
  },
338
387
  }, async () => {
339
388
  await handleLiquidityCommand({
340
- request,
341
389
  from: ctx.message.from.id,
342
390
  id: connectedId,
343
- key: apiKey.key,
344
391
  nodes: allNodes,
345
- reply: ctx.reply,
392
+ reply: n => ctx.reply(n, markdown),
346
393
  text: ctx.message.text,
347
394
  working: () => ctx.replyWithChatAction('typing'),
348
395
  });