caronc / apprise

Apprise - Push Notifications that work with just about every platform!
https://hub.docker.com/r/caronc/apprise
BSD 2-Clause "Simplified" License
11.54k stars 406 forks source link

Add AWS SNS Notification Support #43

Closed x110dc closed 5 years ago

x110dc commented 5 years ago

It would be great to see support for AWS SNS.

caronc commented 5 years ago

Just a note keeping comment here:

boto3 being what appears to be the most wildly used library for AWS SNS messaging.

Possible Notification Formats:

if sns:// isn't an ideal name (as it could become ambiguous if another service provides the similar functionality), maybe awssns:// is a better identifier? or simply aws:// ?

We'll decipher per entry which is a topic from a phone number based on if the field contains all digits (with the exception of r'[)(+-\s]+'

# script usage would be:
apprise -b 'hello world' \
 sns://ABCDEFGHIJK/ZZBADFADFADSFDSA/+15551235678

# But just to be friendly we'd accept spaces, brakets and dashes in the URL
# so this would be safe too:
apprise -b 'hello world' \
 sns://ABCDEFGHIJK/ZZBADFADFADSFDSA/(+1) 555-123-5678

# Multiple Phone #'s, no problem:
apprise -b 'hello world' \
 sns://ABCDEFGHIJK/ZZBADFADFADSFDSA/(+1) 555-123-5678/(+1) 555-321-8765

# Topic Handling
apprise -b 'hello world' \
 sns://ABCDEFGHIJK/ZZBADFADFADSFDSA/Topic1

# Mixing and Matching:
apprise -b 'hello world' \
 sns://ABCDEFGHIJK/ZZBADFADFADSFDSA/Topic1/(+1) 555-321-8765/Topic2/.../.../

I don't have AWS SNS, so i'm basing this on what I've read so far. But it looks like SNS just sends a text message to 1 or more people (and topic based)? Does this sound about right?

Do the URLs identified above seem logical? Do you see any issues and/or caveats? Any comments and/or suggestions? :slightly_smiling_face:

kadrach commented 5 years ago

boto3/botocore have a pretty extensive credential lookup mechanism, from function parameters to various credential files to instance and container metadata services. Ideally apprise would simply use the boto-provided mechanism.

SNS topic names are also not unique and depend on the partition, account and region, e.g. arn:aws:sns:us-east-1:123456789012:my_corporate_topic (aws and aws-cn are the only partitions, I believe).

Then to publish to SNS, apprise would need a connection string containing at least

sns://aws/us-east-1/<accountid>/mytopic
sns://aws-cn/cn-north-1/<accountid>/mytopic

aws-cn only contains two regions so some magic could be worked here to simplify, but a lookup would need to be maintained somehow.

caronc commented 5 years ago

Thanks @kadrach for your feedback! :slightly_smiling_face:

boto3/botocore have a pretty extensive credential lookup mechanism, from function parameters to various credential files to instance and container metadata services. Ideally apprise would simply use the boto-provided mechanism.

I fully agree here; i'll see what i can do... But behind the scenes I see myself doing this in 2 phases.

  1. Phase 1: I'll start with getting it working using boto3 first (as a new dependency). I'll do this on a different branch to at least allow others waiting for this feature to have it working sooner then later. I'll also hope for constructive feedback (both positive and negative) too!
  2. Phase 2: Then (with respect to your comment), I'll see if I can extract 'just' what i need from boto3 and drop the dependency of it. This step may (or may not) involve a lot of effort; and it's this part that concerns me. But at this point i simply haven't researched enough about it yet.

SNS topic names are also not unique and depend on the partition, account and region, e.g. arn:aws:sns:us-east-1:123456789012:my_corporate_topic (aws and aws-cn are the only partitions, I believe).

From what i gather so far, the accountid is irrelevant when accessing the AWS SNS webhooks that apprise would utilize. There is a way to access/generate an aws_access_key and aws_access_secret which are required to interface with AWS SNS from an outside source (the accountid is no longer relevant in these cases). See the Access Keys (Access Key ID and Secret Access Key) here.

With these 2 keys i can access the external API endpoint AWS serves publish the notifications to it with. This appears to be how boto3 does it; so i will probably go this same route. The only early problem I see is that the aws_secret_access_key can contain forward slashes in it... I can see this conflicting with apprise since it's built on URLs.

The boto payloads are so simple and would allow topics to be simplified too (without knowing all of the special colon delimiters). So i would definitely leverage this as well and not require people to remember their long sns subscription ids in the url.

# The simplicity of the underlining code for phase 1
import boto3

# Create an SNS client
client = boto3.client(
    "sns",
    aws_access_key_id="YOUR ACCES KEY",
    aws_secret_access_key="YOUR SECRET KEY",
    region_name="us-east-1"
)
#
# Send your sms message via a Phone No
#
client.publish(
    PhoneNumber="+15552229999",
    Message="Apprise rocks! :)"
)

#
# Send your message via a Topic
#

# Create the topic if it doesn't exist; nothing breaks if it does
topic = client.create_topic(Name="my_topic_name")

# Get the Amazon Resource Name
# this here is that serial you were identifying; but it looks like if
# boto3 can determine if it exists and automatically generate the entire
# string on it's own (account id and everything), then apprise should
# be able to as well (if we get to Phase 2 as discussed above)
topic_arn = topic['TopicArn']

# Publish a message.
client.publish(Message="Apprise really does rock! :)", TopicArn=topic_arn)

I still need to sign up with AWS/SNS; (hopefully there are free accounts/trials) to truly get more details/info for myself. So with that said... I could be really wrong with my statements and assumptions here. :wink:

Please keep the discussion going!

x110dc commented 5 years ago

Thanks for putting in all this effort and doing so so quickly! I'm happy to help test, review or whatever.

And yes, AWS offers a free 1-year account.

caronc commented 5 years ago

By all means; please test away. Everything is set up in it's own branch here. :slightly_smiling_face:

It's Phase No 1 (with respect to my long ranting above). So it includes boto3 as a dependency. I do agree with the previous commenter though, that apprise shouldn't need this (boto3) dependency at all.

So (probably not this weekend), but I'll do my best to drop this dependency and do another update to the branch. But for now, SNS is at least available to people who want it under this branch.

caronc commented 5 years ago

This request turned out to be a really tough one... I think learned more about AWS v4 API Security then I ever wanted too! :sweat:

The good news is I think I'm done. apprise should now fully support AWS SNS (both SMS and Topics) notifications without any boto3 dependencies! I was able to send myself a text message without any problem. :grinning:

I'd be really interested in anyone's feedback. You'll need to use this branch to it test with. Documentation as to how the protocol works can be found here. The syntax is slightly different then what was identified above. It follows this logic now:

sns://{AccessKeyID}/{AccessKeySecret}/{Region}/+{PhoneNo}
sns://{AccessKeyID}/{AccessKeySecret}/{Region}/+{PhoneNo1}/+{PhoneNo2}/+{PhoneNoN}
sns://{AccessKeyID}/{AccessKeySecret}/{Region}/#{Topic}
sns://{AccessKeyID}/{AccessKeySecret}/{Region}/#{Topic1}/#{Topic2}/#{TopicN}

You can mix and match these entries as well:

sns://{AccessKeyID}/{AccessKeySecret}/{Region}/+{PhoneNo1}/#{Topic1}