-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathworker-sender.ts
More file actions
151 lines (138 loc) · 6.69 KB
/
Copy pathworker-sender.ts
File metadata and controls
151 lines (138 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import "reflect-metadata";
import { SendQueue } from "./entity/SendQueue";
import { GroundControlToMajorTom } from "./class/GroundControlToMajorTom";
import { TokenConfiguration } from "./entity/TokenConfiguration";
import { NOTIFICATION_CATEGORY_TRANSACTION, NOTIFICATION_LEVEL_NEWS, NOTIFICATION_LEVEL_PRICE, NOTIFICATION_LEVEL_TIPS, NOTIFICATION_LEVEL_TRANSACTIONS } from "./openapi/constants";
import dataSource from "./data-source";
import { components } from "./openapi/api";
require("dotenv").config();
if (!process.env.FCM_SERVER_KEY || !process.env.APNS_P8 || !process.env.APNS_TOPIC || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID) {
console.error("not all env variables set");
process.exit();
}
process
.on("unhandledRejection", (reason, p) => {
console.error(reason, "Unhandled Rejection at Promise", p);
process.exit(1);
})
.on("uncaughtException", (err) => {
console.error(err, "Uncaught Exception thrown");
process.exit(1);
});
dataSource
.initialize()
.then(async (connection) => {
// start worker
console.log("running groundcontrol worker-sender");
console.log(require("fs").readFileSync("./bowie.txt").toString("ascii"));
const sendQueueRepository = dataSource.getRepository(SendQueue);
const tokenConfigurationRepository = dataSource.getRepository(TokenConfiguration);
while (1) {
// getting random record so multiple workers wont fight for the same record to send
const [record] = await sendQueueRepository .createQueryBuilder()
.orderBy('RAND()') // mysql-specific
.limit(1)
.getMany();
// ^^^ 'order by rand' is suboptimal, but will have to do for now, especially if we are aiming to keep
// queue table near-empty
if (!record) {
await new Promise((resolve) => setTimeout(resolve, Math.floor(Math.random() * (60000 - 1000 + 1)) + 1000, false));
continue;
}
// we atomically lock this record via mariadb's GET_LOCK and typeorm's raw query to allow us
// run multiple sender workers in parallel
const query = `SELECT GET_LOCK(?, ?) as result`;
const result = await sendQueueRepository.query(query, [`send${record.id}`, 0]);
if (result[0].result !== 1) {
process.env.VERBOSE && console.log('could not acquire lock, skipping record');
continue;
}
let payload;
try {
payload = JSON.parse(record.data);
} catch (_) {
process.env.VERBOSE && console.warn("bad json in data:", record.data);
await sendQueueRepository.remove(record);
continue;
}
let tokenConfig = await tokenConfigurationRepository.findOneBy({ os: payload.os, token: payload.token });
if (!tokenConfig) {
if (!payload.os || !payload.token) {
process.env.VERBOSE && console.warn("no os or token in payload:", payload);
await sendQueueRepository.remove(record);
continue;
}
tokenConfig = new TokenConfiguration();
tokenConfig.os = payload.os;
tokenConfig.token = payload.token;
await tokenConfigurationRepository.save(tokenConfig);
}
let unsubscribed = false;
if (!tokenConfig.level_all) unsubscribed = true; // user unsubscribed from all
switch (payload.level) {
case NOTIFICATION_LEVEL_TRANSACTIONS:
if (!tokenConfig.level_transactions) unsubscribed = true;
break;
case NOTIFICATION_LEVEL_NEWS:
if (!tokenConfig.level_news) unsubscribed = true;
break;
case NOTIFICATION_LEVEL_PRICE:
if (!tokenConfig.level_price) unsubscribed = true;
break;
case NOTIFICATION_LEVEL_TIPS:
if (!tokenConfig.level_tips) unsubscribed = true;
break;
}
if (unsubscribed) {
await sendQueueRepository.remove(record);
continue;
}
const timeoutId = setTimeout(() => {
console.error("timeout pushing to token, comitting suicide");
process.exit(2);
}, 21000);
switch (payload.type) {
case 2:
payload = <components["schemas"]["PushNotificationOnchainAddressGotPaid"]>payload;
payload.category = NOTIFICATION_CATEGORY_TRANSACTION;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushOnchainAddressWasPaid(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 3:
payload = <components["schemas"]["PushNotificationOnchainAddressGotUnconfirmedTransaction"]>payload;
payload.category = NOTIFICATION_CATEGORY_TRANSACTION;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushOnchainAddressGotUnconfirmedTransaction(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 1:
payload = <components["schemas"]["PushNotificationLightningInvoicePaid"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushLightningInvoicePaid(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 4:
payload = <components["schemas"]["PushNotificationTxidGotConfirmed"]>payload;
payload.category = NOTIFICATION_CATEGORY_TRANSACTION;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushOnchainTxidGotConfirmed(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 5:
payload = <components["schemas"]["PushNotificationMessage"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushMessage(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
default:
process.env.VERBOSE && console.warn("malformed payload:", payload);
await sendQueueRepository.remove(record);
}
clearTimeout(timeoutId);
}
})
.catch((error) => {
console.error("exception in sender:", error, "comitting suicide");
process.exit(1);
});