backend-manager 3.2.169 → 3.2.171

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.
Files changed (88) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/dist/cli/cli.js +1534 -0
  4. package/dist/manager/functions/core/actions/api/admin/backup.js +338 -0
  5. package/dist/manager/functions/core/actions/api/admin/create-post.js +388 -0
  6. package/dist/manager/functions/core/actions/api/admin/cron.js +37 -0
  7. package/dist/manager/functions/core/actions/api/admin/database-read.js +35 -0
  8. package/dist/manager/functions/core/actions/api/admin/database-write.js +39 -0
  9. package/dist/manager/functions/core/actions/api/admin/edit-post.js +158 -0
  10. package/dist/manager/functions/core/actions/api/admin/firestore-query.js +165 -0
  11. package/dist/manager/functions/core/actions/api/admin/firestore-read.js +38 -0
  12. package/dist/manager/functions/core/actions/api/admin/firestore-write.js +54 -0
  13. package/dist/manager/functions/core/actions/api/admin/get-stats.js +269 -0
  14. package/dist/manager/functions/core/actions/api/admin/payment-processor.js +57 -0
  15. package/dist/manager/functions/core/actions/api/admin/run-hook.js +95 -0
  16. package/dist/manager/functions/core/actions/api/admin/send-notification.js +197 -0
  17. package/dist/manager/functions/core/actions/api/admin/sync-users.js +125 -0
  18. package/dist/manager/functions/core/actions/api/admin/templates/post.html +16 -0
  19. package/dist/manager/functions/core/actions/api/firebase/get-providers.js +102 -0
  20. package/dist/manager/functions/core/actions/api/general/emails/general:download-app-link.js +21 -0
  21. package/dist/manager/functions/core/actions/api/general/fetch-post.js +99 -0
  22. package/dist/manager/functions/core/actions/api/general/generate-uuid.js +41 -0
  23. package/dist/manager/functions/core/actions/api/general/send-email.js +112 -0
  24. package/dist/manager/functions/core/actions/api/handler/create-post.js +146 -0
  25. package/dist/manager/functions/core/actions/api/special/setup-electron-manager-client.js +103 -0
  26. package/dist/manager/functions/core/actions/api/template.js +33 -0
  27. package/dist/manager/functions/core/actions/api/test/authenticate.js +22 -0
  28. package/dist/manager/functions/core/actions/api/test/create-test-accounts.js +27 -0
  29. package/dist/manager/functions/core/actions/api/test/lab.js +55 -0
  30. package/dist/manager/functions/core/actions/api/test/redirect.js +26 -0
  31. package/dist/manager/functions/core/actions/api/test/webhook.js +30 -0
  32. package/dist/manager/functions/core/actions/api/user/create-custom-token.js +32 -0
  33. package/dist/manager/functions/core/actions/api/user/delete.js +68 -0
  34. package/dist/manager/functions/core/actions/api/user/get-active-sessions.js +45 -0
  35. package/dist/manager/functions/core/actions/api/user/get-subscription-info.js +49 -0
  36. package/dist/manager/functions/core/actions/api/user/oauth2/discord.js +114 -0
  37. package/dist/manager/functions/core/actions/api/user/oauth2/google.js +99 -0
  38. package/dist/manager/functions/core/actions/api/user/oauth2.js +476 -0
  39. package/dist/manager/functions/core/actions/api/user/regenerate-api-keys.js +54 -0
  40. package/dist/manager/functions/core/actions/api/user/resolve.js +32 -0
  41. package/dist/manager/functions/core/actions/api/user/sign-out-all-sessions.js +118 -0
  42. package/dist/manager/functions/core/actions/api/user/sign-up copy.js +544 -0
  43. package/dist/manager/functions/core/actions/api/user/sign-up.js +99 -0
  44. package/dist/manager/functions/core/actions/api/user/submit-feedback.js +96 -0
  45. package/dist/manager/functions/core/actions/api/user/validate-settings.js +86 -0
  46. package/dist/manager/functions/core/actions/api.js +354 -0
  47. package/dist/manager/functions/core/actions/create-post-handler.js +184 -0
  48. package/dist/manager/functions/core/actions/generate-uuid.js +62 -0
  49. package/dist/manager/functions/core/actions/sign-up-handler.js +205 -0
  50. package/dist/manager/functions/core/admin/create-post.js +206 -0
  51. package/dist/manager/functions/core/admin/firestore-write.js +72 -0
  52. package/dist/manager/functions/core/admin/get-stats.js +218 -0
  53. package/dist/manager/functions/core/admin/query.js +198 -0
  54. package/dist/manager/functions/core/admin/send-notification.js +206 -0
  55. package/dist/manager/functions/core/cron/daily/ghostii-auto-publisher.js +377 -0
  56. package/dist/manager/functions/core/cron/daily/reset-usage.js +197 -0
  57. package/dist/manager/functions/core/cron/daily.js +114 -0
  58. package/dist/manager/functions/core/events/auth/before-create.js +124 -0
  59. package/dist/manager/functions/core/events/auth/before-signin.js +62 -0
  60. package/dist/manager/functions/core/events/auth/on-create copy.js +121 -0
  61. package/dist/manager/functions/core/events/auth/on-create.js +564 -0
  62. package/dist/manager/functions/core/events/auth/on-delete.js +72 -0
  63. package/dist/manager/functions/core/events/firestore/on-subscription.js +107 -0
  64. package/dist/manager/functions/test/authenticate.js +38 -0
  65. package/dist/manager/functions/test/create-test-accounts.js +144 -0
  66. package/dist/manager/functions/test/webhook.js +37 -0
  67. package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
  68. package/dist/manager/helpers/analytics copy.js +217 -0
  69. package/dist/manager/helpers/analytics.js +467 -0
  70. package/dist/manager/helpers/api-manager.js +324 -0
  71. package/dist/manager/helpers/assistant.js +1043 -0
  72. package/dist/manager/helpers/metadata.js +32 -0
  73. package/dist/manager/helpers/middleware.js +154 -0
  74. package/dist/manager/helpers/roles.js +69 -0
  75. package/dist/manager/helpers/settings.js +158 -0
  76. package/dist/manager/helpers/subscription-resolver-new.js +828 -0
  77. package/dist/manager/helpers/subscription-resolver.js +842 -0
  78. package/dist/manager/helpers/usage.js +381 -0
  79. package/dist/manager/helpers/user.js +198 -0
  80. package/dist/manager/helpers/utilities.js +292 -0
  81. package/dist/manager/index.js +1076 -0
  82. package/dist/manager/libraries/openai.js +460 -0
  83. package/dist/manager/routes/restart/index.js +52 -0
  84. package/dist/manager/routes/test/index.js +43 -0
  85. package/dist/manager/schemas/restart.js +13 -0
  86. package/dist/manager/schemas/test.js +13 -0
  87. package/dist/require.js +3 -0
  88. package/package.json +19 -9
@@ -0,0 +1,72 @@
1
+ let Module = {
2
+ init: async function (Manager, data) {
3
+ this.Manager = Manager;
4
+ this.libraries = Manager.libraries;
5
+ this.assistant = Manager.Assistant({req: data.req, res: data.res})
6
+ this.req = data.req;
7
+ this.res = data.res;
8
+
9
+ return this;
10
+ },
11
+ main: async function() {
12
+ const self = this;
13
+ let libraries = self.libraries;
14
+ let assistant = self.assistant;
15
+ let req = self.req;
16
+ let res = self.res;
17
+ let options = self.assistant.request.data;
18
+ let admin = self.Manager.libraries.admin;
19
+
20
+ let response = {
21
+ status: 200,
22
+ data: {},
23
+ };
24
+
25
+ return libraries.cors(req, res, async () => {
26
+ let user = await assistant.authenticate();
27
+
28
+ // Analytics
29
+ let analytics = self.Manager.Analytics({
30
+ assistant: assistant,
31
+ uuid: user.auth.uid,
32
+ })
33
+ .event({
34
+ category: 'admin',
35
+ action: 'firestore-write',
36
+ // label: '',
37
+ });
38
+
39
+ // Options
40
+ options.path = `${options.path || ''}`
41
+ options.document = options.document || {};
42
+ options.options = options.options || {};
43
+ options.options.merge = typeof options.options.merge === 'undefined' ? true : options.options.merge;
44
+
45
+ if (!user.roles.admin) {
46
+ response.status = 401;
47
+ response.error = new Error('Unauthenticated, admin required.');
48
+ } else if (!options.path) {
49
+ response.status = 401;
50
+ response.error = new Error('Path parameter required');
51
+ } else {
52
+ await admin.firestore().doc(options.path)
53
+ .set(options.document, options.options)
54
+ .then(r => {
55
+ assistant.log(`Wrote to ${options.path}:`, options.document, options.options)
56
+ })
57
+ .catch(e => {
58
+ response.status = 500;
59
+ response.error = e;
60
+ })
61
+ }
62
+
63
+ if (response.status === 200) {
64
+ return res.status(response.status).json(response.data);
65
+ } else {
66
+ assistant.error(response.error)
67
+ return res.status(response.status).send(response.error.message);
68
+ }
69
+ });
70
+ },
71
+ }
72
+ module.exports = Module;
@@ -0,0 +1,218 @@
1
+ let Module = {
2
+ init: async function (Manager, data) {
3
+ this.Manager = Manager;
4
+ this.libraries = Manager.libraries;
5
+ this.assistant = Manager.Assistant({req: data.req, res: data.res})
6
+ this.req = data.req;
7
+ this.res = data.res;
8
+
9
+ return this;
10
+ },
11
+ main: async function() {
12
+ let self = this;
13
+ let libraries = self.libraries;
14
+ let assistant = self.assistant;
15
+ let req = self.req;
16
+ let res = self.res;
17
+
18
+ let response = {
19
+ status: 200,
20
+ data: {},
21
+ error: null,
22
+ };
23
+
24
+ return libraries.cors(req, res, async () => {
25
+ // authenticate admin!
26
+ let user = await assistant.authenticate();
27
+
28
+ // Analytics
29
+ let analytics = self.Manager.Analytics({
30
+ assistant: assistant,
31
+ uuid: user.auth.uid,
32
+ })
33
+ .event({
34
+ category: 'admin',
35
+ action: 'get-stats',
36
+ // label: '',
37
+ });
38
+
39
+ if (!user.roles.admin) {
40
+ response.status = 401;
41
+ response.error = new Error('Unauthenticated, admin required.');
42
+ assistant.error(response.error)
43
+ } else {
44
+ let stats = libraries.admin.firestore().doc(`meta/stats`)
45
+
46
+ await stats
47
+ .get()
48
+ .then(async function (doc) {
49
+ response.data = doc.data() || {};
50
+ await self.fixStats(response.data)
51
+ .catch(e => {
52
+ response.status = 500;
53
+ response.error = new Error(`Failed fixing stats: ${e.message}`);
54
+ assistant.error(response.error)
55
+ })
56
+
57
+ await self.updateStats()
58
+ .catch(e => {
59
+ response.status = 500;
60
+ response.error = new Error(`Failed updating stats: ${e.message}`);
61
+ assistant.error(response.error)
62
+ })
63
+
64
+ await stats
65
+ .get()
66
+ .then(r => {
67
+ response.data = r.data() || {};
68
+ })
69
+ .catch(function (e) {
70
+ response.status = 500;
71
+ response.error = e;
72
+ assistant.error(response.error)
73
+ })
74
+ })
75
+ .catch(function (e) {
76
+ response.status = 500;
77
+ response.error = e;
78
+ assistant.error(response.error)
79
+ })
80
+ }
81
+
82
+ // response.data = data;
83
+
84
+ assistant.log('Stats', assistant.request.data, response);
85
+
86
+ if (response.status === 200) {
87
+ return res.status(response.status).json(response.data);
88
+ } else {
89
+ return res.status(response.status).send(response.error.message);
90
+ }
91
+ });
92
+ },
93
+ fixStats: function (data) {
94
+ let self = this;
95
+ return new Promise(async function(resolve, reject) {
96
+ let stats = self.libraries.admin.firestore().doc(`meta/stats`);
97
+
98
+ if (!data || !data.users || !data.users.total || !data.subscriptions || !data.subscriptions.total) {
99
+ let usersTotal = 0;
100
+ let subscriptionsTotal = 0;
101
+ await self.getAllUsers()
102
+ .then(r => {
103
+ usersTotal = r.length
104
+ })
105
+ .catch(e => {
106
+ response.status = 500;
107
+ response.error = new Error(`Failed fixing stats: ${e.message}`);
108
+ self.assistant.error(response.error);
109
+ })
110
+ await self.getAllSubscriptions()
111
+ .then(r => {
112
+ subscriptionsTotal = r
113
+ })
114
+ .catch(e => {
115
+ response.status = 500;
116
+ response.error = new Error(`Failed fixing stats: ${e.message}`);
117
+ self.assistant.error(response.error);
118
+ })
119
+ await stats
120
+ .set({
121
+ users: {
122
+ total: usersTotal,
123
+ },
124
+ subscriptions: {
125
+ total: subscriptionsTotal,
126
+ },
127
+ }, { merge: true })
128
+ .catch(function (e) {
129
+ return reject(e);
130
+ })
131
+ }
132
+
133
+
134
+ return resolve(data);
135
+ });
136
+ },
137
+ updateStats: function () {
138
+ let self = this;
139
+ return new Promise(async function(resolve, reject) {
140
+ let stats = self.libraries.admin.firestore().doc(`meta/stats`);
141
+ let online = self.libraries.admin.database().ref(`gatherings/online`);
142
+ let onlineCount = 0;
143
+
144
+ await online
145
+ .once('value')
146
+ .then((snap) => {
147
+ let data = snap.val() || {};
148
+ let keys = Object.keys(data);
149
+ onlineCount = keys.length;
150
+ })
151
+ .catch(e => {
152
+ return reject(e);
153
+ })
154
+
155
+ await stats
156
+ .set({
157
+ users: {
158
+ online: onlineCount
159
+ }
160
+ }, { merge: true })
161
+ .catch(function (e) {
162
+ return reject(e);
163
+ })
164
+
165
+ return resolve();
166
+ });
167
+ },
168
+ getAllUsers: function () {
169
+ let self = this;
170
+ return new Promise(async function(resolve, reject) {
171
+ self.users = [];
172
+ await getUsersBatch(self)
173
+ .catch(e => {
174
+ return reject(e);
175
+ })
176
+ return resolve(self.users);
177
+ });
178
+ },
179
+ getAllSubscriptions: function () {
180
+ let self = this;
181
+ return new Promise(async function(resolve, reject) {
182
+ await self.libraries.admin.firestore().collection('notifications/subscriptions/all')
183
+ .get()
184
+ .then(function(querySnapshot) {
185
+ return resolve(querySnapshot.size)
186
+ })
187
+ .catch(function(e) {
188
+ return reject(e)
189
+ });
190
+ });
191
+ }
192
+ }
193
+
194
+ module.exports = Module;
195
+
196
+ function getUsersBatch(self, nextPageToken) {
197
+ return new Promise(async function(resolve, reject) {
198
+ self.libraries.admin.auth().listUsers(1000, nextPageToken)
199
+ .then(function(listUsersResult) {
200
+ self.users = self.users.concat(listUsersResult.users);
201
+ if (listUsersResult.pageToken) {
202
+ // List next batch of users.
203
+ getUsersBatch(self, listUsersResult.pageToken)
204
+ .then(() => {
205
+ return resolve(listUsersResult.users);
206
+ })
207
+ .catch((e) => {
208
+ return reject(e);
209
+ })
210
+ } else {
211
+ return resolve(listUsersResult.users);
212
+ }
213
+ })
214
+ .catch(function(e) {
215
+ return reject(e);
216
+ });
217
+ });
218
+ }
@@ -0,0 +1,198 @@
1
+ const _ = require('lodash');
2
+ const powertools = require('node-powertools');
3
+
4
+ let Module = {
5
+ init: async function (Manager, data) {
6
+ this.Manager = Manager;
7
+ this.libraries = Manager.libraries;
8
+ this.assistant = Manager.Assistant({req: data.req, res: data.res})
9
+ this.req = data.req;
10
+ this.res = data.res;
11
+
12
+ return this;
13
+ },
14
+ main: async function() {
15
+ let self = this;
16
+ let libraries = self.libraries;
17
+ let assistant = self.assistant;
18
+ let req = self.req;
19
+ let res = self.res;
20
+
21
+ let response = {
22
+ status: 200,
23
+ data: {},
24
+ error: null,
25
+ };
26
+
27
+ return libraries.cors(req, res, async () => {
28
+ // authenticate admin!
29
+ let user = await assistant.authenticate();
30
+
31
+ // Analytics
32
+ let analytics = self.Manager.Analytics({
33
+ assistant: assistant,
34
+ uuid: user.auth.uid,
35
+ })
36
+ .event({
37
+ category: 'admin',
38
+ action: 'query',
39
+ // label: '',
40
+ });
41
+
42
+ if (!user.roles.admin) {
43
+ response.status = 401;
44
+ response.error = new Error('Unauthenticated, admin required.');
45
+ assistant.error(response.error)
46
+ } else {
47
+ self.docs = [];
48
+ // assistant.log('Queries', assistant.request.data.queries);
49
+ let queries = powertools.arrayify(assistant.request.data.queries || []);
50
+
51
+ let promises = [];
52
+ for (var i = 0; i < queries.length; i++) {
53
+ queries[i]
54
+ promises.push(self.runQuery(queries[i]))
55
+ }
56
+
57
+ await Promise.all(promises)
58
+ .then((r) => {
59
+ response.data = self.docs;
60
+ // assistant.log('Query result:', );
61
+ })
62
+ .catch((e) => {
63
+ response.error = e;
64
+ response.status = 400;
65
+ assistant.error(response.error)
66
+ })
67
+ }
68
+
69
+ assistant.log('Query', assistant.request.data, response);
70
+
71
+ if (response.status === 200) {
72
+ return res.status(response.status).json(response.data);
73
+ } else {
74
+ return res.status(response.status).send(response.error.message);
75
+ }
76
+ });
77
+ },
78
+ runQuery: runQuery,
79
+ }
80
+ module.exports = Module;
81
+
82
+ // HELPERS //
83
+ async function runQuery(payload) {
84
+ let self = this;
85
+
86
+ payload = payload || {};
87
+ payload.where = powertools.arrayify(payload.where || []);
88
+ payload.filter = powertools.arrayify(payload.filter || []);
89
+ payload.orderBy = powertools.arrayify(payload.orderBy || []);
90
+
91
+ // self.assistant.log('Query', payload);
92
+
93
+ return new Promise(function(resolve, reject) {
94
+ let collection;
95
+
96
+ if (!payload.collection) {
97
+ return resolve([]);
98
+ // return reject(new Error('No collection specified.'));
99
+ }
100
+
101
+ collection = self.libraries.admin.firestore().collection(payload.collection);
102
+
103
+ for (var i = 0; i < payload.where.length; i++) {
104
+ let cur = payload.where[i];
105
+ collection = collection.where(cur.field, cur.operator, cur.value);
106
+ }
107
+ for (var i = 0; i < payload.orderBy.length; i++) {
108
+ let cur = payload.orderBy[i];
109
+ collection = collection.orderBy(cur.field, cur.order)
110
+ }
111
+ if (payload.limit) {
112
+ collection = collection.limit(payload.limit)
113
+ }
114
+ if (payload.startAt) {
115
+ collection = collection.startAt(payload.startAt)
116
+ }
117
+ if (payload.startAfter) {
118
+ collection = collection.startAfter(payload.startAfter)
119
+ }
120
+ if (payload.endAt) {
121
+ collection = collection.endAt(payload.endAt)
122
+ }
123
+ if (payload.endBefore) {
124
+ collection = collection.endBefore(payload.endBefore)
125
+ }
126
+
127
+ collection
128
+ .get()
129
+ .then(function (querySnapshot) {
130
+ querySnapshot.forEach(function (doc) {
131
+
132
+ let exists = self.docs.find(item => {
133
+ return item.path === doc.ref.path
134
+ })
135
+
136
+ if (!exists && checkFilter(doc.data(), payload.filter)) {
137
+ self.docs.push({
138
+ path: doc.ref.path,
139
+ data: doc.data(),
140
+ });
141
+ }
142
+
143
+ });
144
+
145
+ if (payload.filterIndex) {
146
+ let iS = payload.filterIndex[0];
147
+ let iF = payload.filterIndex[1];
148
+ iF = iF > self.docs.length ? self.docs.length - 1 : iF;
149
+ self.docs = self.docs.slice(iS, iF);
150
+ }
151
+
152
+ return resolve(self.docs);
153
+ })
154
+ .catch(function (error) {
155
+ self.assistant.error(error)
156
+ return reject(error);
157
+ });
158
+ });
159
+
160
+ }
161
+
162
+ function checkFilter(data, filter) {
163
+
164
+ // Loop through all filters
165
+ for (var i = 0, l = filter.length; i < l; i++) {
166
+ // Set up field and checks
167
+ let field = `${filter[i].field}`.split(' || ');
168
+ field = powertools.arrayify(field);
169
+ let matches = filter[i].matches || '';
170
+ let regex = powertools.regexify(matches);
171
+
172
+ // Pass/fail
173
+ let innerPassed = false;
174
+
175
+ // Loop through each filter's fields
176
+ for (var i2 = 0, l2 = field.length; i2 < l2; i2++) {
177
+ let fieldInner = field[i2];
178
+ let value = _.get(data, fieldInner, undefined);
179
+
180
+ if (typeof value === 'undefined') {
181
+ innerPassed = false;
182
+ continue;
183
+ } else if (typeof value === 'string') {
184
+ if (value.match(regex)) {
185
+ innerPassed = true;
186
+ break;
187
+ }
188
+ }
189
+ }
190
+
191
+ // If there was not a successful innerPassed then break (innerPassed works on OR logic)
192
+ if (!innerPassed) {
193
+ return false;
194
+ }
195
+
196
+ }
197
+ return true;
198
+ }
@@ -0,0 +1,206 @@
1
+ let Module = {
2
+ init: async function (Manager, data) {
3
+ this.Manager = Manager;
4
+ this.libraries = Manager.libraries;
5
+ this.assistant = Manager.Assistant({req: data.req, res: data.res})
6
+ this.req = data.req;
7
+ this.res = data.res
8
+
9
+ return this;
10
+ },
11
+ main: async function() {
12
+ let self = this;
13
+ let libraries = self.libraries;
14
+ let assistant = self.assistant;
15
+ let req = self.req;
16
+ let res = self.res;
17
+
18
+ let response = {
19
+ status: 200,
20
+ data: {},
21
+ error: null,
22
+ };
23
+
24
+ return libraries.cors(req, res, async () => {
25
+ // authenticate admin!
26
+ let user = await assistant.authenticate();
27
+
28
+ // Analytics
29
+ let analytics = self.Manager.Analytics({
30
+ assistant: assistant,
31
+ uuid: user.auth.uid,
32
+ })
33
+ .event({
34
+ category: 'admin',
35
+ action: 'send-notification',
36
+ // label: '',
37
+ });
38
+
39
+ let payload = self.assistant.request.data.payload || {};
40
+
41
+ if (!payload.title || !payload.body) {
42
+ response.status = 400;
43
+ response.error = new Error('Not enough notification parameters supplied.');
44
+ assistant.error(response.error)
45
+ return res.status(response.status).send(response.error.message);
46
+ }
47
+
48
+ if (!user.roles.admin) {
49
+ response.status = 401;
50
+ response.error = new Error('Unauthenticated, admin required.');
51
+ assistant.error(response.error)
52
+ return res.status(response.status).send(response.error.message);
53
+ } else {
54
+ await self.getTokens({tags: false});
55
+ }
56
+
57
+ assistant.log('Notification', assistant.request.data, response);
58
+
59
+ if (response.status === 200) {
60
+ return res.status(response.status).json(response.data);
61
+ } else {
62
+ return res.status(response.status).send(response.error.message);
63
+ }
64
+
65
+ });
66
+ },
67
+ getTokens: getTokens,
68
+ sendBatch: sendBatch,
69
+ cleanTokens: cleanTokens,
70
+ deleteToken: deleteToken,
71
+ }
72
+ module.exports = Module;
73
+
74
+ // HELPERS //
75
+ let path_processing = 'notifications/processing/all/{notificationId}';
76
+ let path_subscriptions = 'notifications/subscriptions/all';
77
+ let badTokenReasons = ['messaging/invalid-registration-token', 'messaging/registration-token-not-registered']
78
+ let batchPromises = [];
79
+
80
+ function sendBatch(batch, id) {
81
+ let self = this;
82
+ // self.assistant.log(`Sending batch ID: ${id}`, batch);
83
+ self.assistant.log(`Sending batch ID: ${id}`);
84
+
85
+ // self.assistant.log('payload', payload);
86
+ return new Promise(async function(resolve, reject) {
87
+ let payload = {};
88
+ payload.notification = {};
89
+ payload.notification.title = self.assistant.request.data.payload.title;
90
+ payload.notification.click_action = self.assistant.request.data.payload.click_action;
91
+ payload.notification.body = self.assistant.request.data.payload.body;
92
+ payload.notification.icon = self.assistant.request.data.payload.icon;
93
+
94
+ await self.libraries.admin.messaging().sendToDevice(batch, payload)
95
+ .then(async function (response) {
96
+ // self.result.batches.list.push('#' + id + ' | ' + '✅ ' + response.successCount + ' | ' + '❌ ' + response.failureCount);
97
+ self.assistant.log('Sent batch #' + id);
98
+ // self.result.successes += response.successCount;
99
+ // self.result.failures += response.failureCount;
100
+ // console.log('RESP', response);
101
+ if (response.failureCount > 0) {
102
+ await self.cleanTokens(batch, response.results, id);
103
+ }
104
+ resolve();
105
+ })
106
+ .catch(function (e) {
107
+ self.assistant.error('Error sending batch #' + id, e);
108
+ // self.result.status = 'fail';
109
+ reject(e);
110
+ })
111
+ });
112
+ }
113
+
114
+ function getTokens(options) {
115
+ let self = this;
116
+ options = options || {};
117
+ options.tags = options.tags || false;
118
+ return new Promise(async function(resolve, reject) {
119
+ let subs = self.libraries.admin.firestore().collection(path_subscriptions);
120
+ if (options.tags) {
121
+ subs.where('tags', 'array-contains-any', options.tags)
122
+ }
123
+ await subs
124
+ .get()
125
+ .then(function(querySnapshot) {
126
+ self.assistant.log(`Queried ${querySnapshot.size} tokens.`);
127
+ // self.result.subscriptionsStart = querySnapshot.size;
128
+ let batchCurrentSize = 0;
129
+ let batchSizeMax = 1000;
130
+
131
+ let batchCurrent = [];
132
+ let batchLoops = 1;
133
+ batchPromises = [];
134
+
135
+ querySnapshot.forEach(function(doc) {
136
+ // log(self, 'loading... ', batchLoops+'/'+querySnapshot.size);
137
+ if ((batchCurrentSize < batchSizeMax - 1) && (batchLoops < querySnapshot.size)) {
138
+ batchCurrent.push(doc.data().token);
139
+ batchCurrentSize++;
140
+ } else {
141
+ let batchId = batchPromises.length + 1;
142
+ batchCurrent.push(doc.data().token);
143
+ batchCurrentSize++;
144
+ console.log(`Got batch ID: ${batchId} with ${batchCurrentSize} tokens.`);
145
+ batchPromises.push(self.sendBatch(batchCurrent, batchId));
146
+ batchCurrent = [];
147
+ batchCurrentSize = 0;
148
+ }
149
+ batchLoops++;
150
+ });
151
+ })
152
+ .catch(function(e) {
153
+ self.assistant.error('Error querying tokens: ', e)
154
+ reject(error);
155
+ });
156
+
157
+ await Promise.all(batchPromises)
158
+ .then(function(values) {
159
+ self.assistant.log('Finished all batches.');
160
+ })
161
+ .catch(function(e) {
162
+ self.assistant.error('Error sending batches: ', e)
163
+ });
164
+ resolve();
165
+
166
+ });
167
+ }
168
+
169
+ function cleanTokens(batch, results, id) {
170
+ let self = this;
171
+ let cleanPromises = [];
172
+ // self.assistant.log(`Cleaning tokens of batch ID: ${id}`, results);
173
+ self.assistant.log(`Cleaning tokens of batch ID: ${id}`);
174
+ return new Promise(async function(resolve, reject) {
175
+ results.forEach(function (item, index) {
176
+ if (!item.error) { return false; }
177
+ let curCode = item.error.code;
178
+ let token = batch[index];
179
+ self.assistant.log(`Found bad token: ${index} = ${curCode}`);
180
+ if (badTokenReasons.includes(curCode)) {
181
+ cleanPromises.push(self.deleteToken(token, curCode));
182
+ }
183
+ })
184
+ await Promise.all(cleanPromises)
185
+ .catch(function(e) {
186
+ self.assistant.log('error', "Error cleaning failed tokens: ", e);
187
+ });
188
+ resolve();
189
+ });
190
+ }
191
+
192
+ function deleteToken(token, errorCode) {
193
+ let self = this;
194
+ return new Promise(function(resolve, reject) {
195
+ self.libraries.admin.firestore().doc(`${path_subscriptions}/${token}`)
196
+ .delete()
197
+ .then(function() {
198
+ self.assistant.log(`Deleting bad token: ${token} for reason ${errorCode}`);
199
+ resolve();
200
+ })
201
+ .catch(function(error) {
202
+ self.assistant.log('error', `Error deleting bad token: ${token} for reason ${errorCode} because of error ${error}`);
203
+ resolve();
204
+ })
205
+ });
206
+ }