israeli-banks-actual-budget-importer 1.2.5 → 1.3.0

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,3 +1,15 @@
1
+ # [1.3.0](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.2.5...v1.3.0) (2025-05-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update schedule environment variable for periodic imports in docker compose ([82344ef](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/commit/82344ef9d4a4629af884bd8adb87c7cbac87628d))
7
+
8
+
9
+ ### Features
10
+
11
+ * add cron scheduling support for periodic imports and update dependencies ([03fc0c3](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/commit/03fc0c3cee132ab7c513e0547337e5dcce590914))
12
+
1
13
  ## [1.2.5](https://github.com/tomerh2001/israeli-banks-actual-budget-importer/compare/v1.2.4...v1.2.5) (2025-05-12)
2
14
 
3
15
 
package/README.md CHANGED
@@ -30,10 +30,13 @@ services:
30
30
  image: tomerh2001/israeli-banks-actual-budget-importer:latest
31
31
  cap_add:
32
32
  - SYS_ADMIN
33
+ environment:
34
+ - TZ=Asia/Jerusalem
35
+ - SCHEDULE=0 0 * * * # Optional (Used to run periodically - remove to run once)
33
36
  volumes:
34
37
  - ./config.json:/app/config.json
35
38
  - ./cache:/app/cache # Optional
36
- - ./chrome-data:/app/chrome-data # Optional (Can be used to solve 2FA issues like with hapoalim)
39
+ - ./chrome-data:/app/chrome-data # Optional (Used to solve 2FA issues like with hapoalim)
37
40
  ```
38
41
 
39
42
  ### Prerequisites
package/compose.yml CHANGED
@@ -5,9 +5,12 @@ services:
5
5
  cap_add:
6
6
  - SYS_ADMIN
7
7
  platform: linux/amd64
8
- # build:
9
- # context: .
10
- # dockerfile: Dockerfile
8
+ build:
9
+ context: .
10
+ dockerfile: Dockerfile
11
+ environment:
12
+ - TZ=Asia/Jerusalem
13
+ - SCHEDULE=0 0 * * *
11
14
  volumes:
12
15
  - ./config.json:/app/config.json
13
16
  - ./cache:/app/cache
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.5",
2
+ "version": "1.3.0",
3
3
  "name": "israeli-banks-actual-budget-importer",
4
4
  "module": "index.ts",
5
5
  "type": "module",
@@ -26,9 +26,11 @@
26
26
  "packageManager": "yarn@4.9.1",
27
27
  "dependencies": {
28
28
  "@actual-app/api": "^25.5.0",
29
+ "cronstrue": "^2.61.0",
29
30
  "israeli-bank-scrapers": "^5.4.7",
30
31
  "lodash": "^4.17.21",
31
32
  "moment": "^2.30.1",
33
+ "node-cron": "^4.0.7",
32
34
  "p-queue": "^8.1.0",
33
35
  "tsx": "^4.19.4"
34
36
  }
package/src/index.ts CHANGED
@@ -8,27 +8,63 @@ import _ from 'lodash';
8
8
  import actual from '@actual-app/api';
9
9
  import Queue from 'p-queue';
10
10
  import moment from 'moment';
11
+ import cron, {type ScheduledTask, validate} from 'node-cron';
12
+ import cronstrue from 'cronstrue';
11
13
  import config from '../config.json' assert {type: 'json'};
12
14
  import type {ConfigBank} from './config.d.ts';
13
15
  import {scrapeAndImportTransactions} from './utils.ts';
14
16
 
15
- const queue = new Queue({
16
- concurrency: 10,
17
- autoStart: true,
18
- interval: 1000,
19
- intervalCap: 10,
20
- });
17
+ let scheduledTask: ScheduledTask;
21
18
 
22
- await actual.init(config.actual.init);
23
- await actual.downloadBudget(config.actual.budget.syncId, config.actual.budget);
19
+ async function run() {
20
+ const queue = new Queue({
21
+ concurrency: 10,
22
+ autoStart: true,
23
+ interval: 1000,
24
+ intervalCap: 10,
25
+ });
24
26
 
25
- for (const [companyId, bank] of _.entries(config.banks) as Array<[CompanyTypes, ConfigBank]>) {
26
- await queue.add(async () => scrapeAndImportTransactions({companyId, bank}));
27
+ await actual.init(config.actual.init);
28
+ await actual.downloadBudget(config.actual.budget.syncId, config.actual.budget);
29
+
30
+ for (const [companyId, bank] of _.entries(config.banks) as Array<[CompanyTypes, ConfigBank]>) {
31
+ await queue.add(async () => scrapeAndImportTransactions({companyId, bank}));
32
+ }
33
+
34
+ await queue.onIdle();
35
+ await actual.shutdown();
36
+
37
+ console.log('Done');
38
+
39
+ setTimeout(() => process.exit(0), moment.duration(5, 'seconds').asMilliseconds());
27
40
  }
28
41
 
29
- await queue.onIdle();
30
- await actual.shutdown();
42
+ async function safeRun() {
43
+ try {
44
+ await run();
45
+ } catch (error) {
46
+ console.error('Error running scraper:', error);
47
+ } finally {
48
+ if (scheduledTask) {
49
+ printNextRunTime();
50
+ }
51
+ }
52
+ }
31
53
 
32
- console.log('Done');
54
+ function printNextRunTime() {
55
+ const nextRun = scheduledTask.getNextRun();
56
+ console.log('Next run:', moment(nextRun).fromNow(), 'at', moment(nextRun).format('YYYY-MM-DD HH:mm:ss'));
57
+ }
58
+
59
+ if (process.env?.SCHEDULE) {
60
+ if (!validate(process.env.SCHEDULE)) {
61
+ throw new Error(`Invalid cron schedule: ${process.env?.SCHEDULE}`);
62
+ }
33
63
 
34
- setTimeout(() => process.exit(0), moment.duration(5, 'seconds').asMilliseconds());
64
+ console.log('Started scheduled run:', process.env?.SCHEDULE, `(${cronstrue.toString(process.env?.SCHEDULE)})`);
65
+ scheduledTask = cron.schedule(process.env.SCHEDULE, safeRun);
66
+
67
+ printNextRunTime();
68
+ } else {
69
+ await safeRun();
70
+ }