const moment = require('moment'); const { Expo } = require('expo-server-sdk'); const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper'); const models = require('../../core/models'); // Create a new Expo SDK client const expo = new Expo(); const extraMethods = { isValidPushToken: (token) => { return Expo.isExpoPushToken(token); }, sendNotification: async (messages) => { // The Expo push notification service accepts batches of notifications so // that you don't need to send 1000 requests to send 1000 notifications. We // recommend you batch your notifications to reduce the number of requests // and to compress them (notifications with similar content will get // compressed). /** * There is a limit on the number of push notifications (100) you can send at once.Use * `chunkPushNotifications` to divide an array of push notification messages into appropriately * sized chunks */ // Later, after the Expo push notification service has delivered the // notifications to Apple or Google (usually quickly, but allow the the service // up to 30 minutes when under load), a "receipt" for each notification is // created. The receipts will be available for at least a day; stale receipts // are deleted. // // The ID of each receipt is sent back in the response "ticket" for each // notification. In summary, sending a notification produces a ticket, which // contains a receipt ID you later use to get the receipt. // // The receipts may contain error codes to which you must respond. In // particular, Apple or Google may block apps that continue to send // notifications to devices that have blocked notifications or have uninstalled // your app. Expo does not control this policy and sends back the feedback from // Apple and Google so you can handle it appropriately. let chunks = expo.chunkPushNotifications(messages); let tickets = await _sendPushNotificationsAsync(chunks); console.log(tickets); let receiptIds = []; let invalidTokens = []; for (let [key, ticket] of tickets.entries()) { // NOTE: Not all tickets have IDs; for example, tickets for notifications // that could not be enqueued will have error information and no receipt ID. if (ticket.id) { receiptIds.push(ticket); } else { if ((ticket.status === 'error') && (ticket.details.error === 'DeviceNotRegistered')) { invalidTokens.push({ ...messages[key], valid: false, invalidated: moment(), }); } } } console.log(receiptIds); console.log(invalidTokens); let receiptIdChunks = expo.chunkPushNotificationReceiptIds(receiptIds); let xxx = await _getPushNotificationsResultAsync(receiptIdChunks); let notifications = await _saveNotifications(messages, tickets); return new Promise(function (resolve) { resolve(notifications) }); } }; module.exports = generateService(models.Notification, extraMethods); const _sendPushNotificationsAsync = async function (chunks) { let tickets = []; // Send the chunks to the Expo push notification service. There are // different strategies you could use. A simple one is to send one chunk at a // time, which nicely spreads the load out over time: for (let chunk of chunks) { try { let ticketChunk = await expo.sendPushNotificationsAsync(chunk); tickets.push(...ticketChunk); // NOTE: If a ticket contains an error code in ticket.details.error, you // must handle it appropriately. The error codes are listed in the Expo // documentation: // https://docs.expo.io/versions/latest/guides/push-notifications#response-format } catch (error) { console.error(error); } } return new Promise(function (resolve) { resolve(tickets) }); }; const _getPushNotificationsResultAsync = async function (receiptIdChunks) { // Like sending notifications, there are different strategies you could use // to retrieve batches of receipts from the Expo service. let result = []; console.log(receiptIdChunks); for (let chunk of receiptIdChunks) { try { let receipts = await expo.getPushNotificationReceiptsAsync(chunk); console.log('hola', receipts); // The receipts specify whether Apple or Google successfully received the // notification and information about an error, if one occurred. for (let key in receipts) { if (receipts[key].status === 'ok') { result.push[receipts[key]]; continue; } else if (receipts[key].status === 'error') { console.error(`There was an error sending a notification: ${receipts[key].message}`); if (receipts[key].details && receipts[key].details.error) { // The error codes are listed in the Expo documentation: // https://docs.expo.io/versions/latest/guides/push-notifications#response-format // You must handle the errors appropriately. console.error(`The error code is ${receipts[key].details.error}`); } } } } catch (error) { console.error(error); } } return new Promise(function (resolve) { resolve(result) }); } const _saveNotifications = async function (messages, tickets) { let notifications = []; messages.forEach(function (message, index) { let notification = models.NotificationDetail.build({ ...message, ticket: tickets[index].id, status: tickets[index].status, error: (tickets[index].status === 'error') ? tickets[index].details.error : undefined, }); notifications.push(notification); }); return new Promise(function (resolve) { resolve(notifications) }); }