Coder Social home page Coder Social logo

eneris / push-receiver Goto Github PK

View Code? Open in Web Editor NEW

This project forked from matthieulemoine/push-receiver

15.0 2.0 15.0 618 KB

A library to subscribe to GCM/FCM and receive notifications within a node process.

Home Page: https://medium.com/@MatthieuLemoine/my-journey-to-bring-web-push-support-to-node-and-electron-ce70eea1c0b0

License: MIT License

JavaScript 88.35% TypeScript 11.65%

push-receiver's Introduction

push-receiver

A library to subscribe to GCM/FCM and receive notifications within a node process.

When should I use push-receiver ?

  • I want to receive push notifications sent using Firebase Cloud Messaging in an electron desktop application.
  • I want to communicate with a node process/server using Firebase Cloud Messaging infrastructure.

When should I not use push-receiver ?

  • I want to send push notifications (use the firebase SDK instead)
  • My application is running on a FCM supported platform (Android, iOS, Web).

Install

npm i -S @eneris/push-receiver

Requirements

Acknowledgements

Usage

ClientConfig

interface ClientConfig {
    credentials?: Credentials // Will be generated if missing - save this after first use!
    persistentIds?: PersistentId[] // Default - []
    bundleId?: string // Default - 'receiver.push.com'
    chromeId?: string // Default - 'org.chromium.linux'
    chromeVersion?: string // Default - '94.0.4606.51'
    debug?: boolean // Enables debug console logs
    heartbeatIntervalMs?: number // Default - 5 * 60 * 1000
    firebase: FirebaseConfig // Full client firebase credentials are now needed
}

Node example

import { PushReceiver } from '@eneris/push-receiver'

(async () => {
    const instance = new PushReceiver({
        debug: true,
        persistentIds: [], // Recover stored ids of all previous notifications
        firebase: {
            // ...Firebase web credentials
        },
        credentials: null, // Insert credentials here after the first run
    })

    const stopListeningToCredentials = instance.onCredentialsChanged(({ oldCredentials, newCredentials }) => {
        console.log('Client generated new credentials.', newCredentials)
        // Save them somewhere! And decide if thing are needed to re-subscribe
    })

    const stopListeningToNotifications = instance.onNotification(notification => {
        // Do someting with the notification
        console.log('Notification received', notification)
    })

    await instance.connect()

    
    await instance.connect()

    console.log('connected')

    const sender = new PushSender({
        // Firebase service account credentials here
    })

    console.log('server created')

    await sender.testMessage(instance.config.credentials.fcm.token)

    console.log('message sent')

    stopListeningToCredentials()
    stopListeningToNotifications()

    instance.destroy()
})()

push-receiver's People

Contributors

benoist avatar dependabot[bot] avatar dgreif avatar djm181 avatar eneris avatar erikian avatar ibash avatar jjfufu avatar julianhille avatar koush avatar matthieulemoine avatar mderrier avatar panther7 avatar patrick-remy avatar pedrokantar avatar realjax avatar satrik avatar selfisekai avatar tsightler avatar uzitech avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

push-receiver's Issues

PushReceiver socket is recreated after .destroy() is called

An event emitter loop is preventing PushReceiver instances to be gracefully destroyed

reproduction steps

const receiver = new PushReceiver({
      debug: true,
      firebase: firebaseConfig,
      heartbeatIntervalMs: 500, // a short heartbeat interval
      persistentIds: [],
});

await receiver.connect();
await new Promise<void>(resolve => receiver.onReady(resolve));

receiver.on("ON_CONNECT", () => console.log("Connected"));
receiver.on("ON_DISCONNECT", () => console.log("Disconnected"));

const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

await sleep(1_000); // some heartbeats

receiver.destroy();

await sleep(10_000);

// asleep logs:
Disconnected
connect
Connected
Got data: 1
waitForData state: 0
Waiting for 2 more bytes. Got 1
Got data: 65
waitForData state: 0
Processing MCS data: state == 0
VERSION IS 41
RECEIVED PROTO OF TYPE 3
Proto size: 63
waitForData state: 3
Processing MCS data: state == 3
GCM Handshake complete.
waitForData state: 1
Waiting for 2 more bytes. Got 0
Got data: 12
...

IqStanza

I have put the log level to verbose and I see the app getting disconnected and reconnected repeatedly, and I don't get why, is this happening for you as well ?

I thought IQStanzas maybe have a relation to this

so far, I found out that when you receive a IQ Stanzas request, you have to send a response, within a timeout
this is what I found so far for creating a response: https://github.com/igniterealtime/tinder/blob/bef99c006ba35f92fef55cca1e781464e363b0ab/src/main/java/org/xmpp/packet/IQ.java#LL375C29-L375C29

Register request has failed with "Not Found"

I feel like I'm just being dense here, but I just can't get this new version to work. Created a new project and have my firebaseConfig credentials, however, this doesn't even seem to be where the issue is as it fails with the GCM registration. Just using the provided example I put in my firebase credentials, but it doesn't even get to the stage of registering with FCM because the GCM registration fails:

Register request has failed with Error=PHONE_REGISTRATION_ERROR
Retry... 1
Register request has failed with <HTML>
<HEAD>
<TITLE>Not Found</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<!-- GSE Default Error -->
<H1>Not Found</H1>
<H2>Error 404</H2>
</BODY>
</HTML>

This retries 5 times and then eventually exits with this:

/home/tsightler/GitHub/ring-mqtt/node_modules/@eneris/push-receiver/dist/gcm.js:78
            throw new Error('GCM register has failed');
                  ^
Error: GCM register has failed
    at postRegister (/home/tsightler/GitHub/ring-mqtt/node_modules/@eneris/push-receiver/dist/gcm.js:78:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async doRegister (/home/tsightler/GitHub/ring-mqtt/node_modules/@eneris/push-receiver/dist/gcm.js:57:22)
    at async exports.default (/home/tsightler/GitHub/ring-mqtt/node_modules/@eneris/push-receiver/dist/gcm.js:25:25)
    at async PushReceiver.registerIfNeeded (/home/tsightler/GitHub/ring-mqtt/node_modules/@eneris/push-receiver/dist/client.js:163:21)
    at async PushReceiver.connect (/home/tsightler/GitHub/ring-mqtt/node_modules/@eneris/push-receiver/dist/client.js:103:9)
    at async file:///home/tsightler/GitHub/ring-mqtt/push-receiver.js:28:5

Looking through the code, it seems like this portion of the code is unchanged from previous versions, but I'm not sure if legacy GCM registration is working anymore, however, it's possible I'm completely off-base.

PHONE_REGISTRATION_ERROR

Hi, I'm trying to initialize an instance of the push receiver with my app's credentials, but when i start the code i get "Register request has failed with Error=PHONE_REGISTRATION_ERROR"
I don't have any other info except the full output and the code that I'm running

import {PushReceiver} from '@eneris/push-receiver'

const instance = new PushReceiver({
    debug: true,
    persistentIds: [], // Recover stored ids of all previous notifications
    firebase: {
        projectId: 'xxxxxxxxxxxxxxxx-a0000',
        appId: '1:1234567890123:android:a1a1a11a1a1a1a11a',
        apiKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
        messagingSenderId: '00000000000000'
    },
    credentials: undefined, // Insert credentials here after the first run
})

const stopListeningToCredentials = instance.onCredentialsChanged(({ oldCredentials, newCredentials }) => {
    console.log('Client generated new credentials.', newCredentials)
    // Save them somewhere! And decide if thing are needed to re-subscribe
})

const stopListeningToNotifications = instance.onNotification(notification => {
    // Do someting with the notification
    console.log('Notification received', notification)
})

await instance.connect()

console.log('connected')

console.log('server created')

stopListeningToCredentials()
stopListeningToNotifications()
constructor {
  debug: true,
  persistentIds: [],
  firebase: {
      projectId: 'xxxxxxxxxxxxxxxx-a0000',
      appId: '1:1234567890123:android:a1a1a11a1a1a1a11a',
      apiKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
      messagingSenderId: '00000000000000'
  },
  credentials: undefined
}
Register request has failed with Error=PHONE_REGISTRATION_ERROR
Retry... 1
Register request has failed with Error=PHONE_REGISTRATION_ERROR
Retry... 2
Register request has failed with <HTML>
<HEAD>
<TITLE>Not Found</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<!-- GSE Default Error -->
<H1>Not Found</H1>
<H2>Error 404</H2>
</BODY>
</HTML>

Retry... 3
Register request has failed with <HTML>
<HEAD>
<TITLE>Not Found</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<!-- GSE Default Error -->
<H1>Not Found</H1>
<H2>Error 404</H2>
</BODY>
</HTML>

Retry... 4
Register request has failed with <HTML>
<HEAD>
<TITLE>Not Found</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<!-- GSE Default Error -->
<H1>Not Found</H1>
<H2>Error 404</H2>
</BODY>
</HTML>

Retry... 5
Register request has failed with <HTML>
<HEAD>
<TITLE>Not Found</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<!-- GSE Default Error -->
<H1>Not Found</H1>
<H2>Error 404</H2>
</BODY>
</HTML>

/home/chri/IdeaProjects/RegistroAPI/notifications/node_modules/@eneris/push-receiver/dist/gcm.js:78
            throw new Error('GCM register has failed');
                  ^

Error: GCM register has failed
    at postRegister (/home/chri/IdeaProjects/RegistroAPI/notifications/node_modules/@eneris/push-receiver/dist/gcm.js:78:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async doRegister (/home/chri/IdeaProjects/RegistroAPI/notifications/node_modules/@eneris/push-receiver/dist/gcm.js:57:22)
    at async exports.default (/home/chri/IdeaProjects/RegistroAPI/notifications/node_modules/@eneris/push-receiver/dist/gcm.js:25:25)
    at async PushReceiver.registerIfNeeded (/home/chri/IdeaProjects/RegistroAPI/notifications/node_modules/@eneris/push-receiver/dist/client.js:163:21)
    at async PushReceiver.connect (/home/chri/IdeaProjects/RegistroAPI/notifications/node_modules/@eneris/push-receiver/dist/client.js:103:9)
    at async file:///home/chri/IdeaProjects/RegistroAPI/notifications/dist/index.js:21:1

Node.js v22.2.0

Support for emulating iOS

I'm looking into using the library for testing push notifications sent to Android and iOS mobile phones from a Backend service. As far as I can tell from https://github.com/Eneris/push-receiver/blob/master/src/client.ts#L291, the library is simulating an Android device.
Some fields are filtered from the notifications the backend sends, when being received by Android phones (ex. .apns), thus I cannot check if they're set properly.

Would it be feasible to implement iOS emulation as a selectable option?

Node > 20?

Any particular reason this can't run on earlier versions of node?

Invalid object destructuring

In example and readme.

const stopListeningToNotifications = instance.onNotification(({ notification }) => {
        // Do someting with the notification
        console.log('Notification received', notification)
    })

Property 'notification' does not exist on type 'MessageEnvelope'.

New token every App Launch.

I have implemented this library over the original to try and fix a bug where notifications stop working after some time. I am testing that now, but I noticed another item that I would like resolved.

I get a new FCM token every single time I open up my electron application. Using a development build, or a production build, every time the application is closed all the way, then relaunched, the token that I get will be a new token. In every other use for FCM that I have implemented, the token will remain the same between launched unless a manual reset was done, or the app uninstalled and reinstalled. I have not implemented any resets in my code.

This is an issue because I track these, and I view them later for testing and general data gathering, but a new token every time will clutter my data.

I have looked into the library as best i could to determine if I am missing something in the setup/configuration, but I have not found anything I needed to do.

If you need anything from me to help resolve this let me know, I will get it for you.

any uncaught error in onNotification cause push-receiver to become stale and ignore subsequent notifications

Encountered an issue where any uncaught errors thrown within the onNotification event handler cause the push notification receiver to become unresponsive to new notifications. This behavior seems to stem from errors not being properly caught and handled within the EventEmitter used as super (parent class).

As a result, the instance becomes stale silently, without any notice or debugged information, and no further push notifications will be handled by that same instance, effectively causing the receiver to "freeze" and ignore any subsequent notifications.

Steps to Reproduce:

  1. Set up the push-receiver according to the documentation.
  2. Implement an onNotification handler that intentionally throws an error. For example:
pushReceiver.onNotification(() => {
  throw new Error("Test error");
  // or 
  console.log(myObject.someProp) // if myObject is null for some reason
  // or
  callSomeFunctionWithThrowError() 

});

Observe that after the error is thrown, the receiver no longer processes incoming notifications.

Expected Behavior:

PushReceiver should be able to recover from such errors and never become stale.
Errors should be handled internally in the same way as socket errors.
Actual Behavior:
When an error is thrown within the onNotification handler, the receiver stops processing any new notifications, becoming unresponsive.

Possible Solution:

Implementing error handling within the EventEmitter for the onNotification event could potentially resolve half of this issue. The other half might be deeper in the call stack, depending on the hierarchy of the onNotification internal implementation. Additionally, providing a way to catch, observe, or log any errors instead of silently becoming a stale instance would be beneficial. This could involve handling errors internally or providing a callback to observe errors.

Current Workaround

Use Try-Catch block within any on('...') handler, eg:

pushReceiver.onNotification(() => {
  try {
    throw new Error("Test error");
  } catch { ... } 
});

Environment:

Library version: 4.1.3
Node.js version: v20.3.1
Operating System: macOS

This issue is critical for applications relying on continuous push notifications for functionality, as any uncaught error can disrupt the notification flow, impacting user experience.

BTW - thank you for the work, this lib as great value to many apps!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.