notifyr

Using the basic client to make a simple notification bot to alert when a python function is done running or if it fails
from nbdev.showdoc import *

Making a decorator that can send messages

We are going to make a decorator that will store a private key in the keychain and send an encrypted message to itself. The private key will also be assigned to the decorated function as an attribute. The user can then log into any type of nostr client that can receive encrypted DMs to get notifications about python processing results or if the function errors out. Other projects like knockknock offer this sort of service, but take more leg work to get an account set up. Being able to randomly generate a Nostr private key makes this quick and painless.

Note: There is some concern about the safety of the current encrypted message implementation. Please do not use this module for any sort of secure communication. You can see a Github issue on the topic here

Another note about keyring: This project is tested and runs on MacOS - the majority of it seems to build on linux, but seems to have some issues running on Linux due to keyring dependencies. The keyring documenation may help if you attempt to debug. I believe installing kwallet might be the eventual solution.

First let’s make a helper function that will send the message


source

send_nostr_message

 send_nostr_message (notifyr_client:nostrfastr.client.Client, message:str,
                     recipient_pubkey_hex:str)

a simple function that takes a client and a message and sends the message to the client pubkey from the client pubkey

Type Details
notifyr_client Client A client class that will send an encrypted message for us
message str A message that will be encrypted and sent
recipient_pubkey_hex str
Returns None

Next we are going to make helper functions to get and set credentials from keyring


set_password

 set_password (service_name:str, username:str, password:str)

Set password for the user in the specified service.


source

delete_private_key

 delete_private_key ()

delete the nostr hex private key from the computer key ring. This is not used in the decorator function, but may be used if need for testing


source

get_private_key

 get_private_key ()

get the nostr hex private key from the computer key ring


source

set_private_key

 set_private_key (notifyr_privkey_hex:str)

Set the private key in the computer keyring

Type Details
notifyr_privkey_hex str nostr hex private key
Returns None

Let’s test setting and restoring the private key

Clear the private key from keychain

priv_key_hex = get_private_key()
if priv_key_hex is not None:
    delete_private_key()
assert get_private_key() is None

Try setting a new one

priv_key_hex = PrivateKey().hex()
set_private_key(notifyr_privkey_hex = priv_key_hex)
assert get_private_key() == priv_key_hex

Finally we write the decorator function complete with - nostr client handling - start message, success message, error message handling - and setting the private key to the decorator function for easy user access


source

convert_to_hex

 convert_to_hex (pubkey:str)

make sure the pubkey is hex

Type Details
pubkey str hex or npub (bech32) pubkey
Returns str hex pubkey

source

get_notifyr_privkey

 get_notifyr_privkey ()

returns a private key from keychain and sets a new one if one doesn’t exist

/Users/runner/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/fastcore/docscrape.py:225: UserWarning: Unknown section Raises
  else: warn(msg)

source

notifyr

 notifyr (func=None, recipient_pubkey:str=None, relay_urls:list[str]=None)

A decorator that will set a nostr private key to `func.notifyr_privkey_hex and use that key to send an encrypted message to it’s own public key on the start and termination of the decorated function. The output will send whether the function runs completely or ends in an error with an informative message.

Type Default Details
func NoneType None the function to be decorated
recipient_pubkey str None
relay_urls list None
Returns function the decorated function

source

notifyr

 notifyr (func=None, recipient_pubkey:str=None, relay_urls:list[str]=None)

A decorator that will set a nostr private key to `func.notifyr_privkey_hex and use that key to send an encrypted message to it’s own public key on the start and termination of the decorated function. The output will send whether the function runs completely or ends in an error with an informative message.

Type Default Details
func NoneType None the function to be decorated
recipient_pubkey str None
relay_urls list None
Returns function the decorated function

Now we can decorate a couple functions!

pubkey = PrivateKey.from_hex(get_private_key()).public_key.bech32()
@notifyr(recipient_pubkey=pubkey, relay_urls=['ws://127.0.0.1:6969'])
def success():
    return True

@notifyr
def another_success():
    return True

@notifyr
def raise_error():
    raise Exception('Oh no! Process failed!')

And test a successful function

success()
2023-01-08 13:43:02,123 - websocket - WARNING - websocket connected
/Users/ryanarmstrong/python/nostrfastr/nostrfastr/client.py:479: UserWarning: the current implementation of messages should be used with caution
                    see https://github.com/nostr-protocol/nips/issues/107
  warnings.warn('''the current implementation of messages should be used with caution
2023-01-08 13:43:05,131 - websocket - WARNING - websocket connected
True
another_success()
2023-01-08 13:43:08,760 - websocket - WARNING - websocket connected
2023-01-08 13:43:09,101 - websocket - WARNING - websocket connected
2023-01-08 13:43:11,998 - websocket - WARNING - websocket connected
2023-01-08 13:43:12,181 - websocket - WARNING - websocket connected
True
another_success()
2023-01-08 13:43:15,242 - websocket - WARNING - websocket connected
2023-01-08 13:43:15,555 - websocket - WARNING - websocket connected
2023-01-08 13:43:18,802 - websocket - WARNING - websocket connected
2023-01-08 13:43:19,137 - websocket - WARNING - websocket connected
True

And test a failing function

from fastcore.test import test_fail
test_fail(raise_error)
2023-01-08 13:43:24,833 - websocket - WARNING - websocket connected
2023-01-08 13:43:25,179 - websocket - WARNING - websocket connected
2023-01-08 13:43:28,056 - websocket - WARNING - websocket connected
2023-01-08 13:43:28,361 - websocket - WARNING - websocket connected

Remember we can retrieve the private key to log into our messages from the keychain or from success.notifyr_privkey_hex or raise_error.notifyr_privkey_hex.

In this case you will either have to trust me that it works… or verify for yourself!