commit 5cce4b630f7de49bf0ec8bdafcabb8efa2c31502
parent aaa65c104eaf662c2baaaa3aedec33436c3557a3
Author: Matthew Horan <matt@matthoran.com>
Date: Sun, 13 Oct 2019 20:03:44 -0400
First pass at push notification support
* Requires a script be loaded in WeeChat
* Will notify for any message in a PM or highlights in channels
Diffstat:
4 files changed, 117 insertions(+), 0 deletions(-)
diff --git a/scripts/weechatrn.py b/scripts/weechatrn.py
@@ -0,0 +1,73 @@
+import weechat
+import json
+
+weechat.register("WeechatRN", "mhoran", "1.0", "MIT",
+ "WeechatRN push notification plugin", "", "")
+
+# Plugin options
+# /set plugins.var.python.weechatrn.push_token
+script_options = {
+ "push_token": ""
+}
+
+for option, default_value in script_options.items():
+ if weechat.config_is_set_plugin(option):
+ script_options[option] = weechat.config_get_plugin(option)
+ else:
+ weechat.config_set_plugin(option, default_value)
+
+# Register a custom command so the relay can set the token if the relay is
+# configured to blacklist certain commands (like /set).
+def weechatrn_cb(data, buffer, args):
+ weechat.config_set_plugin("push_token", args)
+ return weechat.WEECHAT_RC_OK
+
+hook = weechat.hook_command("weechatrn", "", "", "", "", "weechatrn_cb", "")
+
+# Reset in-memory push token on config change.
+def config_cb(data, option, value):
+ if option == "plugins.var.python.weechatrn.push_token":
+ script_options["push_token"] = value
+ return weechat.WEECHAT_RC_OK
+
+weechat.hook_config("plugins.var.python.weechatrn.*", "config_cb", "")
+
+# Only notify for PMs or highlights if message is not tagged with notify_none
+# (ignores messages from ourselves).
+def priv_msg_cb(data, buffer, date, tags, displayed, highlight, prefix,
+ message):
+ if "notify_none" in tags.split(","):
+ return weechat.WEECHAT_RC_OK
+
+ body = u"<%s> %s" % (prefix, message)
+ is_pm = weechat.buffer_get_string(buffer, "localvar_type") == "private"
+ if is_pm:
+ send_push(title="Private message from %s" % prefix, body=body)
+ elif int(highlight) and weechat.current_buffer() != buffer:
+ buffer_name = (weechat.buffer_get_string(buffer, "short_name") or
+ weechat.buffer_get_string(buffer, "name"))
+ send_push(title="Highlight in %s" % buffer_name, body=body)
+
+ return weechat.WEECHAT_RC_OK
+
+weechat.hook_print("", "irc_privmsg", "", 1, "priv_msg_cb", "")
+
+# Send push notification to Expo server. Message JSON encoded in the format:
+# { "to": "EXPO_PUSH_TOKEN",
+# "title": "Notification title",
+# "body": "Notification body" }
+def process_expo_cb(data, command, return_code, out, err):
+ return weechat.WEECHAT_RC_OK
+
+def send_push(title, body):
+ push_token = script_options["push_token"]
+ if push_token == "":
+ return
+
+ post_body = { "to": push_token, "title": title, "body": body }
+ options = {
+ "httpheader": "Content-Type: application/json",
+ "postfields": json.dumps(post_body) }
+ weechat.hook_process_hashtable(
+ "url:https://exp.host/--/api/v2/push/send",
+ options, 20000, "process_expo_cb", "")
diff --git a/src/lib/helpers/push-notifications.ts b/src/lib/helpers/push-notifications.ts
@@ -0,0 +1,33 @@
+import { Notifications } from 'expo';
+import * as Permissions from 'expo-permissions';
+
+export const registerForPushNotificationsAsync = async () => {
+ const { status: existingStatus } = await Permissions.getAsync(
+ Permissions.NOTIFICATIONS
+ );
+
+ // only ask if permissions have not already been determined, because
+ // iOS won't necessarily prompt the user a second time.
+ if (existingStatus !== 'granted') {
+ // Android remote notification permissions are granted during the app
+ // install, so this will only ask on iOS
+ await Permissions.askAsync(Permissions.NOTIFICATIONS);
+ }
+}
+
+export const getPushNotificationStatusAsync = async () => {
+ const { status: existingStatus } = await Permissions.getAsync(
+ Permissions.NOTIFICATIONS
+ );
+ let finalStatus = existingStatus;
+
+ // Stop here if the user did not grant permissions
+ if (finalStatus !== 'granted') {
+ return;
+ }
+
+ // Get the token that uniquely identifies this device
+ let token = await Notifications.getExpoPushTokenAsync();
+
+ return token;
+}
diff --git a/src/usecase/App.tsx b/src/usecase/App.tsx
@@ -21,6 +21,7 @@ import BufferContainer from "./buffers/ui/BufferContainer";
import BufferList from "./buffers/ui/BufferList";
import { StoreState } from "../store";
import { renderWeechatFormat } from "../lib/weechat/color-formatter";
+import { registerForPushNotificationsAsync } from "../lib/helpers/push-notifications"
interface Props {
buffers: { [key: string]: WeechatBuffer };
@@ -109,6 +110,8 @@ class App extends React.Component<Props, State> {
} else {
this.drawer.openDrawer();
}
+
+ registerForPushNotificationsAsync();
}
componentWillUnmount() {
diff --git a/src/usecase/Root.tsx b/src/usecase/Root.tsx
@@ -8,6 +8,7 @@ import { store, persistor } from "../store";
import App from "./App";
import ConnectionGate from "./ConnectionGate";
+import { getPushNotificationStatusAsync } from "../lib/helpers/push-notifications"
interface State {
connecting: boolean;
@@ -25,6 +26,12 @@ export default class WeechatNative extends React.Component<{}, State> {
this.connection = new WeechatConnection(store.dispatch);
}
+ setNotificationToken = async () => {
+ let token = await getPushNotificationStatusAsync();
+ if (token)
+ this.sendMessageToBuffer("core.weechat", "/weechatrn " + token);
+ }
+
onConnectionSuccess = connection => {
this.setState({ connecting: false });
connection.send("(hotlist) hdata hotlist:gui_hotlist(*)");
@@ -33,6 +40,7 @@ export default class WeechatNative extends React.Component<{}, State> {
);
// connection.send("(nicklist) nicklist");
connection.send("sync");
+ this.setNotificationToken();
};
onConnectionError = reconnect => {