Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifico Standalone Support Added #1275

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 94 additions & 47 deletions apprise/plugins/notifico.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

# Notifico allows you to relay notifications into IRC channels.
# Notifico has since gone offline, but became an open source project.

# Legacy instructions would identify the following:
#
# 1. visit https://n.tkte.ch and sign up for an account
# 2. create a project; either manually or sync with github
Expand All @@ -40,13 +42,16 @@
#
# This plugin also supports taking the URL (as identified above) directly
# as well.
#
# Today, the service can be self hosted: https://notifico.tech/

import re
import requests

from .base import NotifyBase
from ..common import NotifyType
from ..utils.parse import parse_bool, validate_regex
from ..url import PrivacyMode
from ..locale import gettext_lazy as _


Expand Down Expand Up @@ -93,20 +98,17 @@ class NotifyNotifico(NotifyBase):
service_name = 'Notifico'

# The services URL
service_url = 'https://n.tkte.ch'
service_url = 'https://notifico.tech/'

# The default protocol
protocol = 'notifico'

# The default secure protocol
secure_protocol = 'notifico'
secure_protocol = 'notificos'

# A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_notifico'

# Plain Text Notification URL
notify_url = 'https://n.tkte.ch/h/{proj}/{hook}'

# The title is not used
title_maxlen = 0

Expand All @@ -115,11 +117,36 @@ class NotifyNotifico(NotifyBase):

# Define object templates
templates = (
'{schema}://{project_id}/{msghook}',
'{schema}://{host}/{project_id}/{msghook}',
'{schema}://{host}:{port}/{project_id}/{msghook}',
'{schema}://{user}@{host}/{project_id}/{msghook}',
'{schema}://{user}@{host}:{port}/{project_id}/{msghook}',
'{schema}://{user}:{password}@{host}/{project_id}/{msghook}',
'{schema}://{user}:{password}@{host}:{port}/{project_id}/{msghook}',
)

# Define our template arguments
template_tokens = dict(NotifyBase.template_tokens, **{
'host': {
'name': _('Hostname'),
'type': 'string',
'required': True,
},
'port': {
'name': _('Port'),
'type': 'int',
'min': 1,
'max': 65535,
},
'user': {
'name': _('Username'),
'type': 'string',
},
'password': {
'name': _('Password'),
'type': 'string',
'private': True,
},
# The Project ID is found as the first part of the URL
# /1234/........................
'project_id': {
Expand Down Expand Up @@ -155,6 +182,13 @@ class NotifyNotifico(NotifyBase):
'type': 'bool',
'default': True,
},

'token': {
'alias_of': 'msghook',
},
'project': {
'alias_of': 'project_id',
},
})

def __init__(self, project_id, msghook, color=True, prefix=True,
Expand Down Expand Up @@ -189,11 +223,6 @@ def __init__(self, project_id, msghook, color=True, prefix=True,
# Send colors
self.color = color

# Prepare our notification URL now:
self.api_url = self.notify_url.format(
proj=self.project_id,
hook=self.msghook,
)
return

@property
Expand All @@ -219,8 +248,28 @@ def url(self, privacy=False, *args, **kwargs):
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))

return '{schema}://{proj}/{hook}/?{params}'.format(
schema=self.secure_protocol,
# Determine Authentication
auth = ''
if self.user and self.password:
auth = '{user}:{password}@'.format(
user=NotifyNotifico.quote(self.user, safe=''),
password=self.pprint(
self.password, privacy, mode=PrivacyMode.Secret,
safe=''),
)
elif self.user:
auth = '{user}@'.format(
user=NotifyNotifico.quote(self.user, safe=''),
)

default_port = 443 if self.secure else 80

return '{schema}://{auth}{host}{port}/{proj}/{hook}/?{params}'.format(
schema=self.secure_protocol if self.secure else self.protocol,
auth=auth,
host=self.host,
port='' if self.port is None or self.port == default_port
else ':{}'.format(self.port),
proj=self.pprint(self.project_id, privacy, safe=''),
hook=self.pprint(self.msghook, privacy, safe=''),
params=NotifyNotifico.urlencode(params),
Expand Down Expand Up @@ -286,16 +335,31 @@ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
),
}

auth = None
if self.user:
auth = (self.user, self.password)

# Set our schema
schema = 'https' if self.secure else 'http'

url = '%s://%s' % (schema, self.host)
if isinstance(self.port, int):
url += ':%d' % self.port

# Prepare our URL
url += f'/h/{self.project_id}/{self.msghook}'

self.logger.debug('Notifico GET URL: %s (cert_verify=%r)' % (
self.api_url, self.verify_certificate))
url, self.verify_certificate))
self.logger.debug('Notifico Payload: %s' % str(payload))

# Always call throttle before any remote server i/o is made
self.throttle()

try:
r = requests.get(
self.api_url,
self.url,
auth=auth,
params=payload,
headers=headers,
verify=self.verify_certificate,
Expand Down Expand Up @@ -345,16 +409,22 @@ def parse_url(url):
# We're done early as we couldn't load the results
return results

# The first token is stored in the hostname
results['project_id'] = NotifyNotifico.unquote(results['host'])
# Acquire our path
tokens = NotifyNotifico.split_path(results['fullpath'])

# Get Message Hook
try:
results['msghook'] = NotifyNotifico.split_path(
results['fullpath'])[0]
# The last token is our message hook
if 'token' in results['qsd'] and len(results['qsd']['token']):
results['msghook'] = results['qsd']['token']

elif tokens:
results['msghook'] = tokens.pop()

except IndexError:
results['msghook'] = None
# Now acquire our project_id
if 'project' in results['qsd'] and len(results['qsd']['project']):
results['project_id'] = results['qsd']['project']

elif tokens:
results['project_id'] = tokens.pop()

# Include Color
results['color'] = \
Expand All @@ -365,26 +435,3 @@ def parse_url(url):
parse_bool(results['qsd'].get('prefix', True))

return results

@staticmethod
def parse_native_url(url):
"""
Support https://n.tkte.ch/h/PROJ_ID/MESSAGE_HOOK/
"""

result = re.match(
r'^https?://n\.tkte\.ch/h/'
r'(?P<proj>[0-9]+)/'
r'(?P<hook>[A-Z0-9]+)/?'
r'(?P<params>\?.+)?$', url, re.I)

if result:
return NotifyNotifico.parse_url(
'{schema}://{proj}/{hook}/{params}'.format(
schema=NotifyNotifico.secure_protocol,
proj=result.group('proj'),
hook=result.group('hook'),
params='' if not result.group('params')
else result.group('params')))

return None
46 changes: 27 additions & 19 deletions test/test_plugin_notifico.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,71 +43,79 @@
('notifico://:@/', {
'instance': TypeError,
}),
('notifico://1234', {
('notifico://example.com/1234', {
# Just a project id provided (no message token)
'instance': TypeError,
}),
('notifico://abcd/ckhrjW8w672m6HG', {
('notifico://example.com/abcd/ckhrjW8w672m6HG', {
# an invalid project id provided
'instance': TypeError,
}),
('notifico://1234/ckhrjW8w672m6HG', {
('notifico://example.com/1234/%34^j$', {
# A project id and invalid message hook provided
'instance': TypeError,
}),
('notifico://example.com/1234/ckhrjW8w672m6HG', {
# A project id and message hook provided
'instance': NotifyNotifico,
}),
('notifico://1234/ckhrjW8w672m6HG?prefix=no', {
('notifico://example.com/1234/ckhrjW8w672m6HG?prefix=no', {
# Disable our prefix
'instance': NotifyNotifico,
}),
('notifico://1234/ckhrjW8w672m6HG?color=yes', {
('notifico://[email protected]:20/1234/ckhrjW8w672m6HG?color=yes', {
'instance': NotifyNotifico,
'notify_type': 'info',
}),
('notifico://1234/ckhrjW8w672m6HG?color=yes', {
('notificos://user:[email protected]/1234/ckhrjW8w672m6HG?color=yes', {
'instance': NotifyNotifico,
'notify_type': 'success',
}),
('notifico://1234/ckhrjW8w672m6HG?color=yes', {
('notificos://user:[email protected]/'
'?project=1234&token=ckhrjW8w672m6HG&color=yes', {
'instance': NotifyNotifico,
'notify_type': 'success',

# Our expected url(privacy=True) startswith() response:
'privacy_url': 'notificos://user:****@example.com/1...4/c...G'}),

('notifico://example.com/1234/ckhrjW8w672m6HG?color=yes', {
'instance': NotifyNotifico,
'notify_type': 'warning',
}),
('notifico://1234/ckhrjW8w672m6HG?color=yes', {
('notificos://example.com/1234/ckhrjW8w672m6HG?color=yes', {
'instance': NotifyNotifico,
'notify_type': 'failure',
}),
('notifico://1234/ckhrjW8w672m6HG?color=yes', {
('notifico://example.com/1234/ckhrjW8w672m6HG?color=yes', {
'instance': NotifyNotifico,
'notify_type': 'invalid',
}),
('notifico://1234/ckhrjW8w672m6HG?color=no', {
('notifico://example.com/1234/ckhrjW8w672m6HG?color=no', {
# Test our color flag by having it set to off
'instance': NotifyNotifico,

# Our expected url(privacy=True) startswith() response:
'privacy_url': 'notifico://1...4/c...G',
}),
# Support Native URLs
('https://n.tkte.ch/h/2144/uJmKaBW9WFk42miB146ci3Kj', {
'instance': NotifyNotifico,
'privacy_url': 'notifico://example.com/1...4/c...G',
}),
('notifico://1234/ckhrjW8w672m6HG', {
('notifico://example.com/1234/ckhrjW8w672m6HG', {
'instance': NotifyNotifico,
# don't include an image by default
'include_image': False,
}),
('notifico://1234/ckhrjW8w672m6HG', {
('notifico://example.com/1234/ckhrjW8w672m6HG', {
'instance': NotifyNotifico,
# force a failure
'response': False,
'requests_response_code': requests.codes.internal_server_error,
}),
('notifico://1234/ckhrjW8w672m6HG', {
('notifico://example.com/1234/ckhrjW8w672m6HG', {
'instance': NotifyNotifico,
# throw a bizzare code forcing us to fail to look it up
'response': False,
'requests_response_code': 999,
}),
('notifico://1234/ckhrjW8w672m6HG', {
('notifico://example.com/1234/ckhrjW8w672m6HG', {
'instance': NotifyNotifico,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them
Expand Down
Loading