Coder Social home page Coder Social logo

healthjoy / async-firebase Goto Github PK

View Code? Open in Web Editor NEW
36.0 36.0 14.0 444 KB

The lightweight asynchronous client that makes interaction with Firebase Cloud Messaging oversimplified.

License: MIT License

Makefile 1.52% Python 98.48%
async-firebase asyncio firebase python python3

async-firebase's People

Contributors

akalex avatar bigbugboy avatar bokshitsky avatar dependabot[bot] avatar eighthcolor22 avatar festinuz avatar kijk2869 avatar narquadah avatar ndmytro avatar sergeyrudenko111 avatar tomerbin avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

async-firebase's Issues

Improve Push Multicast to take a list of messages

First of all, thank you for this great job.

I want to send multicast push notifications to hundreds of devices and each device has a specific notification_count also the motivation body could include the name of the device's user. With the current implementation of push_multicast(), it's impossible as the function only takes a list of device tokens, not a list of bodies and notification_count.

The solution I propose is to make push_multicast() accept a list of Messages, similar to what firebase_admin does with the send_all() function. This fixes the issue because each message can be personalized.

For now, the only alternative I used was to call hundreds of push() by chunks of 10 in a loop. This is at the cost of performance. I expect it to be a temporary solution.

Invalid JSON payload received. Unknown name "tokens" at \'message\': Cannot find field

        client = cls.get_client()
        message = client.build_android_config(
            data=data,
            title="WOMO",
            body=_(notification),
            priority="high",
            click_action="FLUTTER_NOTIFICATION_CLICK",
        )
        result = await client.push_multicast(device_tokens=tokens, android=message)

get this result

{'error': {'code': 400, 'message': 'Invalid JSON payload received. Unknown name "tokens" at \'message\': Cannot find field.', 'status': 'INVALID_ARGUMENT', 'details': [{'@type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'field': 'message', 'description': 'Invalid JSON payload received. Unknown name "tokens" at \'message\': Cannot find field.'}]}]}}

Python 3.12 Support

I've already submitted a pull request (PR #64) to add Python 3.12 support to the "async-firebase" library. This issue is just to track the progress and facilitate discussions related to the PR. Your feedback and collaboration on the PR are much appreciated.

Data values force-converted to string

Describe the bug
All data values are getting converted to string in the payload, even None/null. There's no way to send data={"foo": null} as a payload.

To Reproduce

        android_config: AndroidConfig = fcm_client.build_android_config(
            priority="normal",
            data={"foo": None}
        )

        response: FcmPushResponse = await fcm_client.push(device_token=device, android=android_config)

This results in the following message being sent:

{
  "message": {
    "token": <censored>,
    "android": {
      "priority": "normal",
      "ttl": "604800s",
      "data": {
        "foo": "None"
      }
    }
  },
  "validate_only": false
}

Expected behavior
I'd expect the message generated to look like this:

{
  "message": {
    "token": <censored>,
    "android": {
      "priority": "normal",
      "ttl": "604800s",
      "data": {
        "foo": null
      }
    }
  },
  "validate_only": false
}

Screenshots
N/A

Additional context
It looks like the issue is https://github.com/healthjoy/async-firebase/blob/master/async_firebase/client.py#L218, which explicitly casts every value in the data key to a string. Could this be changed to remove the cast, or at least exempt nulls from it? The whole point of the data key is to be able to pass arbitrary JSON data to your application and let the app interpret it how you wish. Force-converting everything to string makes it impossible to send some valid JSON messages.

[Question] Limits has no effect?

I probably get it wrong, but it looks like Limits for client have no effect because we create a new httpx.AsyncClient with each AsyncClientBase.send_request call.
I want to limit number of connections to FCM by limiting the number of connections in httpx connection pool, but appereantly for this to work we need to reuse httpx.AsyncClient beetween send_request calls

btw. @akalex thank you for your great work.

[Question] Can this library be used for receiving messages?

Is your feature request related to a problem? Please describe.

I am setting up a server, and I would like to be able to receive messages. The two scenarios where this could come in handy are:

  1. integration testing, the server can be used to mock an android/ios device.
  2. I need to connect to a specific instance of a server, let's say, the raspberry pi on my home, easily.

Describe the solution you'd like
A similar solution as we have for iOS, Android, Flutter, etc. You can register your device, get a token, and receive messages quickly.

Additional context
Maybe that's already possible, but the doc seems more focused on sending messages to mobile devices, hence my question. There is some documentation in Firebase for enabling this in a cloud server, but I didn't fully understand it: https://firebase.google.com/docs/cloud-messaging/server

send_multicast causes 404 response

Describe the bug
404 Response when using send_multicast using the newest version. It works fine on 3.3.0.

To Reproduce
Use send_multicast method.

Additional context
Logs:

[INFO] HTTP Request: POST https://oauth2.googleapis.com/token "HTTP/1.1 200 OK"
[INFO] HTTP Request: POST https://fcm.googleapis.com/v1/projects/xxxxxxxxx/messages%3Asend/batch "HTTP/1.1 404 Not Found"
[WARNING] Unexpected HTTP response with status: 404; body: b''

Thanks!

Image argument for Notification object

Is your feature request related to a problem? Please describe.
The Firebase Notification object in the python Admin SDK is able to take in an "image" argument to display as part of the notification. I would like to be able to use this field when constructing a notification with the async-firebase library as well.

Describe the solution you'd like
The async-firebase Notification object should accept title, body, and image arguments to match the underlying Firebase Notification object

Describe alternatives you've considered
Alternatives would just involve not using the async library and reverting to using the Firebase Notification object, but I would miss the functionality of this library!

Additional context
N/A

Python 3.12 deprecated usage of `datetime.utcnow()`

Describe the bug
Python 3.12 deprecated usage of datetime.utcnow():

datetime: datetime.datetime’s utcnow() and utcfromtimestamp() are deprecated and will be removed in a future version. Instead, use timezone-aware objects to represent datetimes in UTC: respectively, call now() and fromtimestamp() with the tz parameter set to datetime.UTC. (Contributed by Paul Ganssle in gh-103857.)

It may be a good idea to use tz-aware datetimes now.

To Reproduce
E.g. run AsyncFirebaseClient.build_apns_config(...)

.venv/lib/python3.12/site-packages/async_firebase/client.py:232: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
    "apns-expiration": str(int(datetime.utcnow().timestamp()) + ttl),

Expected behavior
No warnings are logged.

Desktop:

  • OS: Ubuntu 22
  • Browser Not relevant
  • Package Version: 3.8.0

Smartphone:

  • Not relevant

'message': 'The registration token is not a valid FCM registration token'

I tried to send a push notification to my Android device, but I get the following response:
{'error': {'code': 400, 'message': 'The registration token is not a valid FCM registration token', 'status': 'INVALID_ARGUMENT', 'details': [{'@type': 'type.googleapis.com/google.firebase.fcm.v1.FcmError', 'errorCode': 'INVALID_ARGUMENT'}]}}

Any help on that?

Url building error

After updating to the latest version of the library, we're encountering an error while sending messages.
Instead of https://fcm.googleapis.com//v1/projects/our-project-id/messages:send built url looks like https://fcm.googleapis.com/v1%2Fprojects%2Four-project-id%2Fmessages:send.
This URL format results in a 404 (Not Found) error from the FCM API.

We have tested this issue with Python versions 3.11 and 3.12.

httpx.ConnectTimeout problem

Приветствую!
Я пишу приложение на FastApi, и хочу использовать в нем Вашу библиотеку.
К сожалению, я получаю ошибку соединения.

OS Linux
async-firebase | 2.6.1 |  

Код класса, работающего с FCM, следующий:

class AIOFireBaseReposytory():
    def __init__(self):

        self.account_file = r"./etd-service-firebase-adminsdk-60lxs-fb9fec3fd9.json"
        self.client = AsyncFirebaseClient()
        self.client.creds_from_service_account_file(self.account_file)



    async def send_message(self, push_message: MessageEntity,
                           devices: list[DeviceEntity]):
        message = Notification(title=push_message.title, body=push_message.body)

        android_config = self.client.build_android_config(
            priority="high",
            ttl=2419200,
            collapse_key="push",
            title=push_message.title,
            body=push_message.body,
        )

        response = await self.client.push_multicast(device_tokens=devices,
                                                    android=android_config
                                                    )

Traceback основной ошибки:


  File "/home/mike_pol/PycharmProjects/fcmnew/venv/bin/lib/python3.10/site-packages/anyio/_core/_sockets.py", line 186, in connect_tcp
    addr_obj = ip_address(remote_host)
  File "/usr/lib/python3.10/ipaddress.py", line 54, in ip_address
    raise ValueError(f'{address!r} does not appear to be an IPv4 or IPv6 address')
ValueError: 'oauth2.googleapis.com' does not appear to be an IPv4 or IPv6 address

Can't send push-notification to topic

Can't send push-notification to topic

Code for reproduce:


from async_firebase import AsyncFirebaseClient
from async_firebase.messages import Message


async def main():
    client = AsyncFirebaseClient()
    client.creds_from_service_account_file("file")

    android_config = client.build_android_config(
        priority="high",
        ttl=2419200,
        collapse_key="push",
        title="TEST",
        body="TEST",
        icon="https://happycorp.com/logo.png",
    )

    message = Message(android=android_config, topic="weather")
    response = await client.send(message)

    print(response.success, response.message_id)


if __name__ == "__main__":
    asyncio.run(main())

Error:

    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/tikey/.pyenv/versions/3.11.1/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/Users/tikey/PycharmProjects/firebase-notify-test/main.py", line 20, in main
    message = Message(android=android_config, topic="weather")
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: Message.__init__() missing 1 required positional argument: 'token'

To fix need allow None value for async_firebase.messages.Message.token like:
token: str = field(default=None)

Http2

Thanks for a great project

I have a question about your usage of httpx client. As I understand from FCM documentation, after upgrading to HTTPv1 API the recommended way to send batch messages is to use multiplexing via HTTP/2 protocol.
To use it with httpx you need to install httpx[http2] and pass http2 flag to client

httpx.AsyncClient(http2=True)

otherwise it establishes connection with HTTP1.1(now it works such way)

So, my question is it was a design choice not to use http2(for some reasons) or just missed?

Multicast response with 400 Bad Request produces UnknownError

Describe the bug
Invalid values in data argument of client.push_multicast() leads to UnknownError('Unexpected error has happened when hitting the FCM API').

To Reproduce

import asyncio
import typing

from async_firebase import AsyncFirebaseClient

async def main():
    client = AsyncFirebaseClient()
    client.creds_from_service_account_file('./config.json')

    device_token = 'xxxxxxx'

    android_config = client.build_android_config(
        priority='high',
        ttl=60,
        title='Test message',
        body='This is a test',
    )
    data: typing.Dict[str, typing.Any] = {'user_id': 10}
    response = await client.push_multicast(device_tokens=[device_token], data=data, android=android_config, dry_run=True)

    print(response.failure_count, response.responses[0].exception)

if __name__ == "__main__":
    asyncio.run(main())

Expected behavior
Proper handling of the error with specific exception with error details because they are provided by FCM in response:

b'\r\n--batch_H3WKviwlw1OiFBuquMNPomHJtcBwS2Oi\r\nContent-Type: application/http\r\nContent-ID: response-37b4e119-2d98-4544-852d-082e429c18c2\r\n\r\nHTTP/1.1 400 Bad Request\r\nVary: Origin\r\nVary: X-Origin\r\nVary: Referer\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n{\n "error": {\n "code": 400,\n "message": "Invalid value at \'message.data[1].value\' (TYPE_STRING), 10",\n "status": "INVALID_ARGUMENT",\n "details": [\n {\n "@type": "type.googleapis.com/google.rpc.BadRequest",\n "fieldViolations": [\n {\n "field": "message.data[1].value",\n "description": "Invalid value at \'message.data[1].value\' (TYPE_STRING), 10"\n }\n ]\n }\n ]\n }\n}\n\r\n--batch_H3WKviwlw1OiFBuquMNPomHJtcBwS2Oi--\r\n'

python: 3.8
async-frebase: 2.0.2

ValueError: ``messages.PushNotification`` cannot be assembled as data has not been provided


        android_config = client.build_android_config(
            priority="high",
            ttl=2419200,
            collapse_key="push",
            data={"discount": "15%", "key_1": "value_1", "timestamp": "2021-02-24T12:00:15"},
            title="Store Changes",
            body="Recent store changes",
        )

        result = await client.push_multicast(device_tokens=tokens, android=android_config)

get these error

File ~/Code/chat_python_lib/chat_python_lib/chat/firebase.py:66, in AsyncGoogleMessageClient.send_message(cls, data, notification, tokens)
     50 message = client.build_android_config(
     51     data=data,
     52     title="WOMO",
   (...)
     55     click_action="FLUTTER_NOTIFICATION_CLICK",
     56 )
     57 android_config = client.build_android_config(
     58     priority="high",
     59     ttl=2419200,
   (...)
     63     body="Recent store changes",
     64 )
---> 66 result = await client.push_multicast(device_tokens=tokens, android=android_config)
     67 return result

File ~/Code/chat_python_lib/.venv/lib64/python3.8/site-packages/async_firebase/client.py:445, in AsyncFirebaseClient.push_multicast(self, device_tokens, android, apns, data, notification, webpush, dry_run)
    431     raise ValueError(
    432         f"A single ``messages.MulticastMessage`` may contain up to {MULTICAST_MESSAGE_MAX_DEVICE_TOKENS} "
    433         "device tokens."
    434     )
    436 message = MulticastMessage(
    437     tokens=device_tokens,
    438     data=data or {},
   (...)
    442     apns=apns,
    443 )
--> 445 push_notification = self.assemble_push_notification(apns_config=apns, dry_run=dry_run, message=message)
    446 response = await self._send_request(push_notification)
    447 return response.json()

File ~/Code/chat_python_lib/.venv/lib64/python3.8/site-packages/async_firebase/client.py:96, in AsyncFirebaseClient.assemble_push_notification(apns_config, message, dry_run)
     94 if len(push_notification["message"]) == 1:
     95     logging.warning("No data has been provided to construct push notification payload")
---> 96     raise ValueError("``messages.PushNotification`` cannot be assembled as data has not been provided")
     97 return push_notification

ValueError: ``messages.PushNotification`` cannot be assembled as data has not been provided

404 on send_each_for_multicast

Recently, when sending multicast message, AsyncFirebaseClient would close the event loop and stop working further.

I did some digging myself and found out, that it happens because of 404 response when making a "message:send" request.
Discovered cause of this was async_firebase/utils.py:49 util that has encoded a ":" symbol into "%3A", resulting in a 404 response.

Removing quote fixed the issue.

image

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.