├── .gitignore ├── MANIFEST.in ├── README.md ├── fcm_notification ├── __init__.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── fcm_notification │ ├── __init__.py │ └── doctype │ │ ├── __init__.py │ │ ├── fcm_notification_settings │ │ ├── __init__.py │ │ ├── fcm_notification_settings.js │ │ ├── fcm_notification_settings.json │ │ ├── fcm_notification_settings.py │ │ └── test_fcm_notification_settings.py │ │ └── user_device │ │ ├── __init__.py │ │ ├── test_user_device.py │ │ ├── user_device.js │ │ ├── user_device.json │ │ └── user_device.py ├── hooks.py ├── modules.txt ├── patches.txt ├── send_notification.py └── templates │ ├── __init__.py │ └── pages │ └── __init__.py ├── license.txt ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.egg-info 4 | *.swp 5 | tags 6 | fcm_notification/docs/current -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include fcm_notification *.css 8 | recursive-include fcm_notification *.csv 9 | recursive-include fcm_notification *.html 10 | recursive-include fcm_notification *.ico 11 | recursive-include fcm_notification *.js 12 | recursive-include fcm_notification *.json 13 | recursive-include fcm_notification *.md 14 | recursive-include fcm_notification *.png 15 | recursive-include fcm_notification *.py 16 | recursive-include fcm_notification *.svg 17 | recursive-include fcm_notification *.txt 18 | recursive-exclude fcm_notification *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FCM Notification for ERPNext 2 | Send notifications created in Frappe or ERPNext as push notication via Firebase Cloud Message(FCM) 3 | 4 | ### Steps to use the app: 5 | 6 | 1. Install the app into your site. [(Refer)](https://frappeframework.com/docs/v13/user/en/bench/frappe-commands#app-installation) 7 | 8 | 2. Create a new Server Script with values given below
9 | i. Script Type: **DocType Event**
10 | ii. Reference Document Type: **Notification Log**
11 | iii. DocType Event: **Before Insert**
12 | iv. Script: `frappe.call("fcm_notification.send_notification.send_notification", doc=doc)`
13 | To learn more about server scripts [see this link.](https://frappeframework.com/docs/v13/user/en/desk/scripting/server-script) 14 | 15 | 2. Add your FCM server key in FCM Notification Settings. [(Refer)](https://intercom.help/push-monkey/en/articles/1649592-how-to-set-up-your-fcm-keys-previously-called-gcm) 16 | 17 | 3. Link your device id to each user using the **User Device** DocType. 18 | 19 | 4. Optionally create a notification in Frappe/ERPNext. [(Refer)](https://docs.erpnext.com/docs/v12/user/manual/en/setting-up/notifications) 20 | 21 | 5. Run an event that triggers any notification. The notifcation will be send the respetive user via FCM if they have subscribed to it. 22 | 23 | 24 | ## Supporting Organization 25 | 26 | The development of this app was commissioned by [Searchosis marketing Pvt Ltd](searchosis.com) 27 | 28 | 29 | -------------------------------------------------------------------------------- /fcm_notification/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | __version__ = '0.0.1' 3 | 4 | -------------------------------------------------------------------------------- /fcm_notification/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/config/__init__.py -------------------------------------------------------------------------------- /fcm_notification/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | def get_data(): 4 | return [ 5 | { 6 | "module_name": "Fcm Notification", 7 | "color": "grey", 8 | "icon": "octicon octicon-file-directory", 9 | "type": "module", 10 | "label": _("Fcm Notification") 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /fcm_notification/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | # source_link = "https://github.com/[org_name]/fcm_notification" 6 | # docs_base_url = "https://[org_name].github.io/fcm_notification" 7 | # headline = "App that does everything" 8 | # sub_heading = "Yes, you got that right the first time, everything" 9 | 10 | def get_context(context): 11 | context.brand_html = "Fcm Notification" 12 | -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/fcm_notification/__init__.py -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/fcm_notification/doctype/__init__.py -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/fcm_notification_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/fcm_notification/doctype/fcm_notification_settings/__init__.py -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/fcm_notification_settings/fcm_notification_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Raheeb and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('FCM Notification Settings', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | }); 9 | -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/fcm_notification_settings/fcm_notification_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2022-02-06 15:25:27.453748", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "settings_section", 10 | "server_key" 11 | ], 12 | "fields": [ 13 | { 14 | "fieldname": "settings_section", 15 | "fieldtype": "Section Break", 16 | "label": "Settings" 17 | }, 18 | { 19 | "fieldname": "server_key", 20 | "fieldtype": "Data", 21 | "label": "Server Key", 22 | "length": 250 23 | } 24 | ], 25 | "index_web_pages_for_search": 1, 26 | "issingle": 1, 27 | "links": [], 28 | "modified": "2022-02-06 15:25:43.703960", 29 | "modified_by": "Administrator", 30 | "module": "Fcm Notification", 31 | "name": "FCM Notification Settings", 32 | "owner": "Administrator", 33 | "permissions": [ 34 | { 35 | "create": 1, 36 | "delete": 1, 37 | "email": 1, 38 | "print": 1, 39 | "read": 1, 40 | "role": "System Manager", 41 | "share": 1, 42 | "write": 1 43 | } 44 | ], 45 | "sort_field": "modified", 46 | "sort_order": "DESC" 47 | } -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/fcm_notification_settings/fcm_notification_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Raheeb and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | class FCMNotificationSettings(Document): 8 | pass 9 | -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/fcm_notification_settings/test_fcm_notification_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Raheeb and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | class TestFCMNotificationSettings(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/user_device/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/fcm_notification/doctype/user_device/__init__.py -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/user_device/test_user_device.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Raheeb and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | class TestUserDevice(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/user_device/user_device.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Raheeb and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('User Device', { 5 | // refresh: function(frm) { 6 | 7 | // } 8 | }); 9 | -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/user_device/user_device.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "autoname": "format: DEV-{#######}", 5 | "creation": "2022-02-06 15:46:19.185765", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": [ 10 | "user", 11 | "device_name", 12 | "device_id" 13 | ], 14 | "fields": [ 15 | { 16 | "fieldname": "user", 17 | "fieldtype": "Link", 18 | "label": "User", 19 | "options": "User" 20 | }, 21 | { 22 | "fieldname": "device_name", 23 | "fieldtype": "Data", 24 | "label": "Device Name" 25 | }, 26 | { 27 | "fieldname": "device_id", 28 | "fieldtype": "Data", 29 | "in_list_view": 1, 30 | "label": "Device ID", 31 | "length": 250, 32 | "reqd": 1, 33 | "unique": 1 34 | } 35 | ], 36 | "index_web_pages_for_search": 1, 37 | "links": [], 38 | "modified": "2022-02-06 15:46:19.185765", 39 | "modified_by": "Administrator", 40 | "module": "Fcm Notification", 41 | "name": "User Device", 42 | "owner": "Administrator", 43 | "permissions": [ 44 | { 45 | "create": 1, 46 | "delete": 1, 47 | "email": 1, 48 | "export": 1, 49 | "print": 1, 50 | "read": 1, 51 | "report": 1, 52 | "role": "System Manager", 53 | "share": 1, 54 | "write": 1 55 | } 56 | ], 57 | "sort_field": "modified", 58 | "sort_order": "DESC" 59 | } -------------------------------------------------------------------------------- /fcm_notification/fcm_notification/doctype/user_device/user_device.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Raheeb and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | class UserDevice(Document): 8 | pass 9 | -------------------------------------------------------------------------------- /fcm_notification/hooks.py: -------------------------------------------------------------------------------- 1 | from . import __version__ as app_version 2 | 3 | app_name = "fcm_notification" 4 | app_title = "Fcm Notification" 5 | app_publisher = "Raheeb" 6 | app_description = "Sends Frappe notifications to devices via Firebase Cloud Message" 7 | app_icon = "octicon octicon-file-directory" 8 | app_color = "grey" 9 | app_email = "rahibhassan.10@gmail.com" 10 | app_license = "MIT" 11 | 12 | # Includes in 13 | # ------------------ 14 | 15 | # include js, css files in header of desk.html 16 | # app_include_css = "/assets/fcm_notification/css/fcm_notification.css" 17 | # app_include_js = "/assets/fcm_notification/js/fcm_notification.js" 18 | 19 | # include js, css files in header of web template 20 | # web_include_css = "/assets/fcm_notification/css/fcm_notification.css" 21 | # web_include_js = "/assets/fcm_notification/js/fcm_notification.js" 22 | 23 | # include custom scss in every website theme (without file extension ".scss") 24 | # website_theme_scss = "fcm_notification/public/scss/website" 25 | 26 | # include js, css files in header of web form 27 | # webform_include_js = {"doctype": "public/js/doctype.js"} 28 | # webform_include_css = {"doctype": "public/css/doctype.css"} 29 | 30 | # include js in page 31 | # page_js = {"page" : "public/js/file.js"} 32 | 33 | # include js in doctype views 34 | # doctype_js = {"doctype" : "public/js/doctype.js"} 35 | # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} 36 | # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} 37 | # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} 38 | 39 | # Home Pages 40 | # ---------- 41 | 42 | # application home page (will override Website Settings) 43 | # home_page = "login" 44 | 45 | # website user home page (by Role) 46 | # role_home_page = { 47 | # "Role": "home_page" 48 | # } 49 | 50 | # Generators 51 | # ---------- 52 | 53 | # automatically create page for each record of this doctype 54 | # website_generators = ["Web Page"] 55 | 56 | # Installation 57 | # ------------ 58 | 59 | # before_install = "fcm_notification.install.before_install" 60 | # after_install = "fcm_notification.install.after_install" 61 | 62 | # Uninstallation 63 | # ------------ 64 | 65 | # before_uninstall = "fcm_notification.uninstall.before_uninstall" 66 | # after_uninstall = "fcm_notification.uninstall.after_uninstall" 67 | 68 | # Desk Notifications 69 | # ------------------ 70 | # See frappe.core.notifications.get_notification_config 71 | 72 | # notification_config = "fcm_notification.notifications.get_notification_config" 73 | 74 | # Permissions 75 | # ----------- 76 | # Permissions evaluated in scripted ways 77 | 78 | # permission_query_conditions = { 79 | # "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", 80 | # } 81 | # 82 | # has_permission = { 83 | # "Event": "frappe.desk.doctype.event.event.has_permission", 84 | # } 85 | 86 | # DocType Class 87 | # --------------- 88 | # Override standard doctype classes 89 | 90 | # override_doctype_class = { 91 | # "ToDo": "custom_app.overrides.CustomToDo" 92 | # } 93 | 94 | # Document Events 95 | # --------------- 96 | # Hook on document methods and events 97 | 98 | # doc_events = { 99 | # "*": { 100 | # "on_update": "method", 101 | # "on_cancel": "method", 102 | # "on_trash": "method" 103 | # } 104 | # } 105 | 106 | doc_events = { 107 | "Notification Log": { 108 | "before_insert": "fcm_notification.send_notification.send_notification" 109 | } 110 | } 111 | 112 | # Scheduled Tasks 113 | # --------------- 114 | 115 | # scheduler_events = { 116 | # "all": [ 117 | # "fcm_notification.tasks.all" 118 | # ], 119 | # "daily": [ 120 | # "fcm_notification.tasks.daily" 121 | # ], 122 | # "hourly": [ 123 | # "fcm_notification.tasks.hourly" 124 | # ], 125 | # "weekly": [ 126 | # "fcm_notification.tasks.weekly" 127 | # ] 128 | # "monthly": [ 129 | # "fcm_notification.tasks.monthly" 130 | # ] 131 | # } 132 | 133 | # Testing 134 | # ------- 135 | 136 | # before_tests = "fcm_notification.install.before_tests" 137 | 138 | # Overriding Methods 139 | # ------------------------------ 140 | # 141 | # override_whitelisted_methods = { 142 | # "frappe.desk.doctype.event.event.get_events": "fcm_notification.event.get_events" 143 | # } 144 | # 145 | # each overriding function accepts a `data` argument; 146 | # generated from the base implementation of the doctype dashboard, 147 | # along with any modifications made in other Frappe apps 148 | # override_doctype_dashboards = { 149 | # "Task": "fcm_notification.task.get_dashboard_data" 150 | # } 151 | 152 | # exempt linked doctypes from being automatically cancelled 153 | # 154 | # auto_cancel_exempted_doctypes = ["Auto Repeat"] 155 | 156 | 157 | # User Data Protection 158 | # -------------------- 159 | 160 | user_data_fields = [ 161 | { 162 | "doctype": "{doctype_1}", 163 | "filter_by": "{filter_by}", 164 | "redact_fields": ["{field_1}", "{field_2}"], 165 | "partial": 1, 166 | }, 167 | { 168 | "doctype": "{doctype_2}", 169 | "filter_by": "{filter_by}", 170 | "partial": 1, 171 | }, 172 | { 173 | "doctype": "{doctype_3}", 174 | "strict": False, 175 | }, 176 | {"doctype": "{doctype_4}"}, 177 | ] 178 | 179 | # Authentication and authorization 180 | # -------------------------------- 181 | 182 | # auth_hooks = [ 183 | # "fcm_notification.auth.validate" 184 | # ] 185 | -------------------------------------------------------------------------------- /fcm_notification/modules.txt: -------------------------------------------------------------------------------- 1 | Fcm Notification -------------------------------------------------------------------------------- /fcm_notification/patches.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/patches.txt -------------------------------------------------------------------------------- /fcm_notification/send_notification.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | import requests 3 | import json 4 | from frappe import enqueue 5 | import re 6 | 7 | 8 | def user_id(doc): 9 | user_email = doc.for_user 10 | user_device_id = frappe.get_all( 11 | "User Device", filters={"user": user_email}, fields=["device_id"] 12 | ) 13 | return user_device_id 14 | 15 | 16 | @frappe.whitelist() 17 | def send_notification(doc, event): 18 | device_ids = user_id(doc) 19 | for device_id in device_ids: 20 | enqueue( 21 | process_notification, 22 | queue="default", 23 | now=False, 24 | device_id=device_id, 25 | notification=doc, 26 | ) 27 | 28 | 29 | def convert_message(message): 30 | CLEANR = re.compile("<.*?>") 31 | cleanmessage = re.sub(CLEANR, "", message) 32 | # cleantitle = re.sub(CLEANR, "",title) 33 | return cleanmessage 34 | 35 | 36 | def process_notification(device_id, notification): 37 | message = notification.email_content 38 | title = notification.subject 39 | if message: 40 | message = convert_message(message) 41 | if title: 42 | title = convert_message(title) 43 | 44 | url = "https://fcm.googleapis.com/fcm/send" 45 | body = { 46 | "to": device_id.device_id, 47 | "notification": {"body": message, "title": title}, 48 | "data": { 49 | "doctype": notification.document_type, 50 | "docname": notification.document_name, 51 | }, 52 | } 53 | 54 | server_key = frappe.db.get_single_value("FCM Notification Settings", "server_key") 55 | auth = f"Bearer {server_key}" 56 | req = requests.post( 57 | url=url, 58 | data=json.dumps(body), 59 | headers={ 60 | "Authorization": auth, 61 | "Content-Type": "application/json", 62 | "Accept": "application/json", 63 | }, 64 | ) 65 | frappe.log_error(req.text) 66 | -------------------------------------------------------------------------------- /fcm_notification/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/templates/__init__.py -------------------------------------------------------------------------------- /fcm_notification/templates/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tridz-dev/erpnext_fcm/0fbd9cabf62c00226ea47ed76c66b6159aa2818a/fcm_notification/templates/pages/__init__.py -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | License: MIT -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # frappe -- https://github.com/frappe/frappe is installed via 'bench init' -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("requirements.txt") as f: 4 | install_requires = f.read().strip().split("\n") 5 | 6 | # get version from __version__ variable in fcm_notification/__init__.py 7 | from fcm_notification import __version__ as version 8 | 9 | setup( 10 | name="fcm_notification", 11 | version=version, 12 | description="Sends Frappe notifications to devices via Firebase Cloud Message", 13 | author="Raheeb", 14 | author_email="rahibhassan.10@gmail.com", 15 | packages=find_packages(), 16 | zip_safe=False, 17 | include_package_data=True, 18 | install_requires=install_requires 19 | ) 20 | --------------------------------------------------------------------------------