mirror of https://github.com/caronc/apprise
Support for t2bot.io webhooks for Matrix (#189)
parent
408810d6b4
commit
8d2a53cedf
|
@ -41,6 +41,7 @@ from ..common import NotifyImageSize
|
||||||
from ..common import NotifyFormat
|
from ..common import NotifyFormat
|
||||||
from ..utils import parse_bool
|
from ..utils import parse_bool
|
||||||
from ..utils import parse_list
|
from ..utils import parse_list
|
||||||
|
from ..utils import validate_regex
|
||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
|
|
||||||
# Define default path
|
# Define default path
|
||||||
|
@ -74,12 +75,16 @@ class MatrixWebhookMode(object):
|
||||||
# Support the slack webhook plugin
|
# Support the slack webhook plugin
|
||||||
SLACK = "slack"
|
SLACK = "slack"
|
||||||
|
|
||||||
|
# Support the t2bot webhook plugin
|
||||||
|
T2BOT = "t2bot"
|
||||||
|
|
||||||
|
|
||||||
# webhook modes are placed ito this list for validation purposes
|
# webhook modes are placed ito this list for validation purposes
|
||||||
MATRIX_WEBHOOK_MODES = (
|
MATRIX_WEBHOOK_MODES = (
|
||||||
MatrixWebhookMode.DISABLED,
|
MatrixWebhookMode.DISABLED,
|
||||||
MatrixWebhookMode.MATRIX,
|
MatrixWebhookMode.MATRIX,
|
||||||
MatrixWebhookMode.SLACK,
|
MatrixWebhookMode.SLACK,
|
||||||
|
MatrixWebhookMode.T2BOT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,6 +127,11 @@ class NotifyMatrix(NotifyBase):
|
||||||
|
|
||||||
# Define object templates
|
# Define object templates
|
||||||
templates = (
|
templates = (
|
||||||
|
# Targets are ignored when using t2bot mode; only a token is required
|
||||||
|
'{schema}://{token}',
|
||||||
|
'{schema}://{user}@{token}',
|
||||||
|
|
||||||
|
# All other non-t2bot setups require targets
|
||||||
'{schema}://{user}:{password}@{host}/{targets}',
|
'{schema}://{user}:{password}@{host}/{targets}',
|
||||||
'{schema}://{user}:{password}@{host}:{port}/{targets}',
|
'{schema}://{user}:{password}@{host}:{port}/{targets}',
|
||||||
'{schema}://{token}:{password}@{host}/{targets}',
|
'{schema}://{token}:{password}@{host}/{targets}',
|
||||||
|
@ -199,8 +209,7 @@ class NotifyMatrix(NotifyBase):
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
def __init__(self, targets=None, mode=None, include_image=False,
|
def __init__(self, targets=None, mode=None, include_image=False, **kwargs):
|
||||||
**kwargs):
|
|
||||||
"""
|
"""
|
||||||
Initialize Matrix Object
|
Initialize Matrix Object
|
||||||
"""
|
"""
|
||||||
|
@ -233,6 +242,16 @@ class NotifyMatrix(NotifyBase):
|
||||||
self.logger.warning(msg)
|
self.logger.warning(msg)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
if self.mode == MatrixWebhookMode.T2BOT:
|
||||||
|
# t2bot configuration requires that a webhook id is specified
|
||||||
|
self.access_token = validate_regex(
|
||||||
|
self.host, r'^[a-z0-9]{64}$', 'i')
|
||||||
|
if not self.access_token:
|
||||||
|
msg = 'An invalid T2Bot/Matrix Webhook ID ' \
|
||||||
|
'({}) was specified.'.format(self.host)
|
||||||
|
self.logger.warning(msg)
|
||||||
|
raise TypeError(msg)
|
||||||
|
|
||||||
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
|
def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
|
||||||
"""
|
"""
|
||||||
Perform Matrix Notification
|
Perform Matrix Notification
|
||||||
|
@ -257,20 +276,30 @@ class NotifyMatrix(NotifyBase):
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Acquire our access token from our URL
|
if self.mode != MatrixWebhookMode.T2BOT:
|
||||||
access_token = self.password if self.password else self.user
|
# Acquire our access token from our URL
|
||||||
|
access_token = self.password if self.password else self.user
|
||||||
|
|
||||||
default_port = 443 if self.secure else 80
|
default_port = 443 if self.secure else 80
|
||||||
|
|
||||||
# Prepare our URL
|
# Prepare our URL
|
||||||
url = '{schema}://{hostname}:{port}/{webhook_path}/{token}'.format(
|
url = '{schema}://{hostname}:{port}/{webhook_path}/{token}'.format(
|
||||||
schema='https' if self.secure else 'http',
|
schema='https' if self.secure else 'http',
|
||||||
hostname=self.host,
|
hostname=self.host,
|
||||||
port='' if self.port is None
|
port='' if self.port is None
|
||||||
or self.port == default_port else self.port,
|
or self.port == default_port else self.port,
|
||||||
webhook_path=MATRIX_V1_WEBHOOK_PATH,
|
webhook_path=MATRIX_V1_WEBHOOK_PATH,
|
||||||
token=access_token,
|
token=access_token,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
#
|
||||||
|
# t2bot Setup
|
||||||
|
#
|
||||||
|
|
||||||
|
# Prepare our URL
|
||||||
|
url = 'https://webhooks.t2bot.io/api/v1/matrix/hook/' \
|
||||||
|
'{token}'.format(token=self.access_token)
|
||||||
|
|
||||||
# Retrieve our payload
|
# Retrieve our payload
|
||||||
payload = getattr(self, '_{}_webhook_payload'.format(self.mode))(
|
payload = getattr(self, '_{}_webhook_payload'.format(self.mode))(
|
||||||
|
@ -381,7 +410,7 @@ class NotifyMatrix(NotifyBase):
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
'displayName':
|
'displayName':
|
||||||
self.user if self.user else self.matrix_default_user,
|
self.user if self.user else self.app_id,
|
||||||
'format': 'html',
|
'format': 'html',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,6 +428,27 @@ class NotifyMatrix(NotifyBase):
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
def _t2bot_webhook_payload(self, body, title='',
|
||||||
|
notify_type=NotifyType.INFO, **kwargs):
|
||||||
|
"""
|
||||||
|
Format the payload for a T2Bot Matrix based messages
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Retrieve our payload
|
||||||
|
payload = self._matrix_webhook_payload(
|
||||||
|
body=body, title=title, notify_type=notify_type, **kwargs)
|
||||||
|
|
||||||
|
# Acquire our image url if we're configured to do so
|
||||||
|
image_url = None if not self.include_image else \
|
||||||
|
self.image_url(notify_type)
|
||||||
|
|
||||||
|
if image_url:
|
||||||
|
# t2bot can take an avatarUrl Entry
|
||||||
|
payload['avatarUrl'] = image_url
|
||||||
|
|
||||||
|
return payload
|
||||||
|
|
||||||
def _send_server_notification(self, body, title='',
|
def _send_server_notification(self, body, title='',
|
||||||
notify_type=NotifyType.INFO, **kwargs):
|
notify_type=NotifyType.INFO, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -867,6 +917,9 @@ class NotifyMatrix(NotifyBase):
|
||||||
))
|
))
|
||||||
self.logger.debug('Matrix Payload: %s' % str(payload))
|
self.logger.debug('Matrix Payload: %s' % str(payload))
|
||||||
|
|
||||||
|
# Initialize our response object
|
||||||
|
r = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = fn(
|
r = fn(
|
||||||
url,
|
url,
|
||||||
|
@ -948,7 +1001,8 @@ class NotifyMatrix(NotifyBase):
|
||||||
"""
|
"""
|
||||||
Ensure we relinquish our token
|
Ensure we relinquish our token
|
||||||
"""
|
"""
|
||||||
self._logout()
|
if self.mode != MatrixWebhookMode.T2BOT:
|
||||||
|
self._logout()
|
||||||
|
|
||||||
def url(self, privacy=False, *args, **kwargs):
|
def url(self, privacy=False, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -997,12 +1051,14 @@ class NotifyMatrix(NotifyBase):
|
||||||
us to substantiate this object.
|
us to substantiate this object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
results = NotifyBase.parse_url(url)
|
results = NotifyBase.parse_url(url, verify_host=False)
|
||||||
|
|
||||||
if not results:
|
if not results:
|
||||||
# We're done early as we couldn't load the results
|
# We're done early as we couldn't load the results
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
if not results.get('host'):
|
||||||
|
return None
|
||||||
|
|
||||||
# Get our rooms
|
# Get our rooms
|
||||||
results['targets'] = NotifyMatrix.split_path(results['fullpath'])
|
results['targets'] = NotifyMatrix.split_path(results['fullpath'])
|
||||||
|
|
||||||
|
@ -1040,4 +1096,37 @@ class NotifyMatrix(NotifyBase):
|
||||||
results['mode'] = results['qsd'].get(
|
results['mode'] = results['qsd'].get(
|
||||||
'mode', results['qsd'].get('webhook'))
|
'mode', results['qsd'].get('webhook'))
|
||||||
|
|
||||||
|
# t2bot detection... look for just a hostname, and/or just a user/host
|
||||||
|
# if we match this; we can go ahead and set the mode (but only if
|
||||||
|
# it was otherwise not set)
|
||||||
|
if results['mode'] is None \
|
||||||
|
and not results['password'] \
|
||||||
|
and not results['targets']:
|
||||||
|
|
||||||
|
# Default mode to t2bot
|
||||||
|
results['mode'] = MatrixWebhookMode.T2BOT
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_native_url(url):
|
||||||
|
"""
|
||||||
|
Support https://webhooks.t2bot.io/api/v1/matrix/hook/WEBHOOK_TOKEN/
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = re.match(
|
||||||
|
r'^https?://webhooks\.t2bot\.io/api/v1/matrix/hook/'
|
||||||
|
r'(?P<webhook_token>[A-Z0-9_-]+)/?'
|
||||||
|
r'(?P<args>\?.+)?$', url, re.I)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
mode = 'mode={}'.format(MatrixWebhookMode.T2BOT)
|
||||||
|
|
||||||
|
return NotifyMatrix.parse_url(
|
||||||
|
'{schema}://{webhook_token}/{args}'.format(
|
||||||
|
schema=NotifyMatrix.secure_protocol,
|
||||||
|
webhook_token=result.group('webhook_token'),
|
||||||
|
args='?{}'.format(mode) if not result.group('args')
|
||||||
|
else '{}&{}'.format(result.group('args'), mode)))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
@ -1241,12 +1241,17 @@ TEST_URLS = (
|
||||||
('matrixs://', {
|
('matrixs://', {
|
||||||
'instance': None,
|
'instance': None,
|
||||||
}),
|
}),
|
||||||
('matrix://localhost', {
|
('matrix://localhost?mode=off', {
|
||||||
# treats it as a anonymous user to register
|
# treats it as a anonymous user to register
|
||||||
'instance': plugins.NotifyMatrix,
|
'instance': plugins.NotifyMatrix,
|
||||||
# response is false because we have nothing to notify
|
# response is false because we have nothing to notify
|
||||||
'response': False,
|
'response': False,
|
||||||
}),
|
}),
|
||||||
|
('matrix://localhost', {
|
||||||
|
# response is TypeError because we'll try to initialize as
|
||||||
|
# a t2bot and fail (localhost is too short of a api key)
|
||||||
|
'instance': TypeError
|
||||||
|
}),
|
||||||
('matrix://user:pass@localhost/#room1/#room2/#room3', {
|
('matrix://user:pass@localhost/#room1/#room2/#room3', {
|
||||||
'instance': plugins.NotifyMatrix,
|
'instance': plugins.NotifyMatrix,
|
||||||
'response': False,
|
'response': False,
|
||||||
|
@ -1310,6 +1315,27 @@ TEST_URLS = (
|
||||||
# user and token specified; image set to True
|
# user and token specified; image set to True
|
||||||
'instance': plugins.NotifyMatrix,
|
'instance': plugins.NotifyMatrix,
|
||||||
}),
|
}),
|
||||||
|
('matrixs://user@{}?mode=t2bot&format=markdown&image=True'
|
||||||
|
.format('a' * 64), {
|
||||||
|
# user and token specified; image set to True
|
||||||
|
'instance': plugins.NotifyMatrix}),
|
||||||
|
('matrix://user@{}?mode=t2bot&format=markdown&image=False'
|
||||||
|
.format('z' * 64), {
|
||||||
|
# user and token specified; image set to True
|
||||||
|
'instance': plugins.NotifyMatrix}),
|
||||||
|
# This will default to t2bot because no targets were specified and no
|
||||||
|
# password
|
||||||
|
('matrixs://{}'.format('c' * 64), {
|
||||||
|
'instance': plugins.NotifyMatrix,
|
||||||
|
# Throws a series of connection and transfer exceptions when this flag
|
||||||
|
# is set and tests that we gracfully handle them
|
||||||
|
'test_requests_exceptions': True,
|
||||||
|
}),
|
||||||
|
# Test Native URL
|
||||||
|
('https://webhooks.t2bot.io/api/v1/matrix/hook/{}/'.format('d' * 64), {
|
||||||
|
# user and token specified; image set to True
|
||||||
|
'instance': plugins.NotifyMatrix,
|
||||||
|
}),
|
||||||
# Legacy (Depricated) image reference
|
# Legacy (Depricated) image reference
|
||||||
('matrixs://user:token@localhost?mode=slack&thumbnail=False', {
|
('matrixs://user:token@localhost?mode=slack&thumbnail=False', {
|
||||||
# user and token specified; image set to True
|
# user and token specified; image set to True
|
||||||
|
@ -1340,7 +1366,12 @@ TEST_URLS = (
|
||||||
# is set and tests that we gracfully handle them
|
# is set and tests that we gracfully handle them
|
||||||
'test_requests_exceptions': True,
|
'test_requests_exceptions': True,
|
||||||
}),
|
}),
|
||||||
|
('matrix://{}/?mode=t2bot'.format('b' * 64), {
|
||||||
|
'instance': plugins.NotifyMatrix,
|
||||||
|
# Throws a series of connection and transfer exceptions when this flag
|
||||||
|
# is set and tests that we gracfully handle them
|
||||||
|
'test_requests_exceptions': True,
|
||||||
|
}),
|
||||||
|
|
||||||
##################################
|
##################################
|
||||||
# NotifyMatterMost
|
# NotifyMatterMost
|
||||||
|
|
Loading…
Reference in New Issue