Compare commits

..

No commits in common. "d6e24aa359e94ceab09bc980e403ff0fa7a2d047" and "1e374c61e6e7859a52212a1b6b93793a764b8b1c" have entirely different histories.

3 changed files with 26 additions and 73 deletions

View File

@ -1,5 +1,5 @@
FROM python:3.11-alpine FROM python:3.11-alpine
RUN python3 -m pip --no-cache-dir install bleach jinja2 markdown matrix-nio RUN python3 -m pip --no-cache-dir install bleach markdown matrix-nio
ADD matrixchat-notify.py /bin/ ADD matrixchat-notify.py /bin/
ADD matrixchat-notify-config.json /etc/ ADD matrixchat-notify-config.json /etc/
RUN chmod +x /bin/matrixchat-notify.py RUN chmod +x /bin/matrixchat-notify.py

View File

@ -40,7 +40,7 @@ steps:
* `allowed_tags` *(default:* [`DEFAULT_ALLOWED_TAGS`]*)* * `allowed_tags` *(default:* [`DEFAULT_ALLOWED_TAGS`]*)*
List or set or string with comma-separated list of HTML tag names. HTML List or set or string with comma-separated list of HTML tag names. HTML
tags not included will be stripped from the HTML output generated by tags not included, will be stripped from the HTML output generated by
rendering a Markdown message template. rendering a Markdown message template.
Note that the default list does not include any tags, which allow to load Note that the default list does not include any tags, which allow to load
@ -64,28 +64,11 @@ steps:
The Matrix homeserver URL. The Matrix homeserver URL.
* `jinja`
If set to `yes`, `y`, `true`, `t`, `on` or `1`, the message template is
rendered with the [Jinja] templating engine (instead of performing simple
placeholder substitution). The template context is controlled by the
`pass_environment` setting, same as with non-Jinja templates, but
placeholders use a different syntax (example: `{{DRONE_REPO}}`), so the
`template` setting should be changed to be a valid Jinja2 template string
when this is enabled.
Using this feature requires the `jinja2` Python module to be available
(it is installed by default in the plugin's docker image).
* `markdown` * `markdown`
If set to `yes`, `y`, `true`, `t`, `on` or `1`, the message resulting from If set to `yes`, `y`, `true` or `on`, the message resulting from template
template substtution is considered to be in Markdown format and will be substtution is considered to be in Markdown format and will be rendered to
rendered to HTML and sent as a formatted message with the format set to HTML and sent as a formatted message with `org.matrix.custom.html` format.
`org.matrix.custom.html`.
Using this feature requires the `markdown` and `bleach` Python modules to
be available (they are installed by default in the plugin's docker image).
* `markdown_extensions` *(default:* `admonition, extra, sane_lists, smarty`) * `markdown_extensions` *(default:* `admonition, extra, sane_lists, smarty`)
@ -112,9 +95,9 @@ steps:
* `template` *(default:* `${DRONE_BUILD_STATUS}`*)* * `template` *(default:* `${DRONE_BUILD_STATUS}`*)*
The message template. Valid placeholders (example: `${DRONE_REPO}`) will be The message template. Valid placeholders of the form `${PLACEHOLDER}` will
substituted with the values of the matching environment variables (subject be substituted with the values of the matching environment variables
to filtering according to the `pass_environment` setting). (subject to filtering according to the `pass_environment` setting).
See this [reference] for environment variables available in drone.io CI See this [reference] for environment variables available in drone.io CI
pipelines. pipelines.
@ -124,11 +107,10 @@ steps:
ID of user on homeserver to send message as (ID, not username). ID of user on homeserver to send message as (ID, not username).
[`DEFAULT_ALLOWED_ATTRS`]: ./matrixchat-notify.py#L27 [`DEFAULT_ALLOWED_ATTRS`]: ./matrixchat-notify.py#L29
[`DEFAULT_ALLOWED_TAGS`]: ./matrixchat-notify.py#L34 [`DEFAULT_ALLOWED_TAGS`]: ./matrixchat-notify.py#L35
[allowed attributes]: https://bleach.readthedocs.io/en/latest/clean.html#allowed-attributes-attributes [allowed attributes]: https://bleach.readthedocs.io/en/latest/clean.html#allowed-attributes-attributes
[drone.io]: https://drone.io/ [drone.io]: https://drone.io/
[jinja]: https://jinja.palletsprojects.com/
[list of extensions]: https://python-markdown.github.io/extensions/ [list of extensions]: https://python-markdown.github.io/extensions/
[plugin]: https://docs.drone.io/plugins/overview/ [plugin]: https://docs.drone.io/plugins/overview/
[reference]: https://docs.drone.io/pipeline/environment/reference/ [reference]: https://docs.drone.io/pipeline/environment/reference/

View File

@ -5,7 +5,6 @@ Requires:
* <https://pypi.org/project/matrix-nio> * <https://pypi.org/project/matrix-nio>
* Optional: <https://pypi.org/project/bleach/> * Optional: <https://pypi.org/project/bleach/>
* Optional: <https://pypi.org/project/Jinja2/>
* Optional: <https://pypi.org/project/markdown/> * Optional: <https://pypi.org/project/markdown/>
""" """
@ -21,47 +20,36 @@ from distutils.util import strtobool
from os.path import exists from os.path import exists
from string import Template from string import Template
import bleach
from nio import AsyncClient, LoginResponse from nio import AsyncClient, LoginResponse
PROG = "matrixchat-notify" PROG = "matrixchat-notify"
CONFIG_FILENAME = f"{PROG}-config.json" CONFIG_FILENAME = f"{PROG}-config.json"
DEFAULT_ALLOWED_ATTRS = { DEFAULT_ALLOWED_ATTRS = bleach.ALLOWED_ATTRIBUTES.copy()
DEFAULT_ALLOWED_ATTRS.update(
{
"*": ["class"], "*": ["class"],
"a": ["href", "title"],
"abbr": ["title"],
"acronym": ["title"],
"img": ["alt", "src"], "img": ["alt", "src"],
} }
DEFAULT_ALLOWED_TAGS = { )
"a", DEFAULT_ALLOWED_TAGS = bleach.ALLOWED_TAGS | {
"abbr",
"acronym",
"b",
"blockquote",
"code",
"dd", "dd",
"div", "div",
"dl", "dl",
"dt", "dt",
"em",
"h1", "h1",
"h2", "h2",
"h3", "h3",
"h4", "h4",
"h5", "h5",
"h6", "h6",
"i",
"li",
"ol",
"p", "p",
"span", "span",
"strong",
"table", "table",
"td", "td",
"th", "th",
"thead", "thead",
"tr", "tr",
"ul",
} }
DEFAULT_HOMESERVER = "https://matrix.org" DEFAULT_HOMESERVER = "https://matrix.org"
DEFAULT_MARKDOWN_EXTENSIONS = "admonition, extra, sane_lists, smarty" DEFAULT_MARKDOWN_EXTENSIONS = "admonition, extra, sane_lists, smarty"
@ -74,7 +62,6 @@ SETTINGS_KEYS = (
"deviceid", "deviceid",
"devicename", "devicename",
"homeserver", "homeserver",
"jinja",
"markdown", "markdown",
"markdown_extensions", "markdown_extensions",
"pass_environment", "pass_environment",
@ -88,7 +75,7 @@ log = logging.getLogger(PROG)
def tobool(s): def tobool(s):
try: try:
return strtobool(str(s)) return strtobool(s)
except ValueError: except ValueError:
return False return False
@ -158,15 +145,15 @@ async def send_notification(config, message):
await client.close() await client.close()
def get_template_context(config): def render_message(config):
pass_environment = config.get("pass_environment", []) pass_environment = config.get("pass_environment", "")
if not isinstance(pass_environment, list): if not isinstance(pass_environment, list):
pass_environment = [pass_environment] pass_environment = [pass_environment]
patterns = [] patterns = []
for value in pass_environment: for value in pass_environment:
# expand any comma-separated names/patterns # expand any comma-separetd names/patterns
if "," in value: if "," in value:
patterns.extend([p.strip() for p in value.split(",") if p.strip()]) patterns.extend([p.strip() for p in value.split(",") if p.strip()])
else: else:
@ -178,28 +165,12 @@ def get_template_context(config):
for pattern in patterns: for pattern in patterns:
filtered_names.update(fnmatch.filter(env_names, pattern)) filtered_names.update(fnmatch.filter(env_names, pattern))
return {name: os.environ[name] for name in tuple(filtered_names)} context = {name: os.environ[name] for name in tuple(filtered_names)}
def render_message(config):
context = get_template_context(config)
template = config.get("template", DEFAULT_TEMPLATE) template = config.get("template", DEFAULT_TEMPLATE)
if tobool(config.get("jinja")):
try:
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()
return env.from_string(template).render(context)
except Exception as exc:
log.error("Could not render Jinja2 template: %s", exc)
return template
else:
return Template(template).safe_substitute(context) return Template(template).safe_substitute(context)
def render_markdown(message, config): def render_markdown(message, config):
import bleach
import markdown import markdown
allowed_attrs = config.get("allowed_attrs", DEFAULT_ALLOWED_ATTRS) allowed_attrs = config.get("allowed_attrs", DEFAULT_ALLOWED_ATTRS)