Skip to content

Commit

Permalink
re-add notifications, but make them mor versatile and configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
Kunsi committed Sep 30, 2024
1 parent 96559e0 commit d7f7067
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 13 deletions.
21 changes: 16 additions & 5 deletions frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
user_is_admin,
)
from ib_hosted import get_scoped_api_key, ib, update_asset_userdata
from notifier import Notifier
from redis_session import RedisSessionStore

app = Flask(
Expand Down Expand Up @@ -323,20 +324,30 @@ def content_request_review(asset_id):
if "state" in asset["userdata"]: # not in new state?
return error("Cannot review")

moderation_message = "{asset} uploaded by {user}. ".format(
user=g.user,
asset=asset["filetype"].capitalize(),
)

if g.user_is_admin:
update_asset_userdata(asset, state=State.CONFIRMED)
app.logger.warn(
"auto-confirming {} because it was uploaded by admin {}".format(
asset["id"], g.user
)
)
return jsonify(ok=True)

moderation_url = url_for("content_moderate", asset_id=asset_id, _external=True)
moderation_message += "It was auto-confirmed because user is an admin."
else:
moderation_url = url_for("content_moderate", asset_id=asset_id, _external=True)
app.logger.info(
"moderation url for {} is {}".format(asset["id"], moderation_url)
)
update_asset_userdata(asset, state=State.REVIEW)
moderation_message += f"Check it at {moderation_url}"

app.logger.info("moderation url for {} is {}".format(asset["id"], moderation_url))
n = Notifier()
n.message(moderation_message)

update_asset_userdata(asset, state=State.REVIEW)
return jsonify(ok=True)


Expand Down
70 changes: 70 additions & 0 deletions notifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from json import dumps
from logging import getLogger

import paho.mqtt.client as mqtt
from requests import post

from conf import CONFIG

LOG = getLogger("Notifier")


class Notifier:
def __init__(self):
self.config = CONFIG.get("NOTIFIER", {})

self.mqtt = None
if self.config.get("MQTT_HOST"):
self.mqtt = mqtt.Client()
if self.config.get("MQTT_USERNAME") and self.config.get("MQTT_PASSWORD"):
self.mqtt.username_pw_set(
self.config["MQTT_USERNAME"], self.config["MQTT_PASSWORD"]
)

def message(self, message, level="INFO", component=None):
LOG.debug(f"{message=} {level=} {component=}")
if self.mqtt:
try:
self._mqtt_message(message, level, component)
except Exception:
LOG.exception("could not send mqtt message")

for ntfy_url in self.config.get("NTFY", set()):
try:
self._ntfy_message(ntfy_url, message)
except Exception:
LOG.exception(f"ntfy url {ntfy_url} failed sending")

def _mqtt_message(self, message, level, component_suffix):
assert self.mqtt is not None

LOG.info("sending mqtt message")

component = "infobeamer-cms"
if component_suffix is not None:
component = f"{component}/{component_suffix}"

payload = {
"level": level,
"component": component,
"msg": message,
}

LOG.debug(f"mqtt payload is {payload!r}")

self.mqtt.connect(self.config["MQTT_HOST"])
self.mqtt.publish(self.config["MQTT_TOPIC"], dumps(payload))
self.mqtt.disconnect()

LOG.info("sent mqtt message")

def _ntfy_message(self, ntfy_url, message):
LOG.info(f"sending alert to {ntfy_url} with message {message!r}")

r = post(
ntfy_url,
data=str(message).encode("utf-8"),
)
r.raise_for_status()

LOG.info(f"ntfy url {ntfy_url} returned {r.status_code}")
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ wrapt==1.16.0
zope.event==5.0
zope.interface==6.3
rtoml==0.10.0
paho-mqtt==2.1.0
27 changes: 20 additions & 7 deletions settings.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ URL_KEY = 'reallysecure'
# Uncomment to use redis as a session store
# REDIS_HOST = 'localhost'

# Push notifications for moderation requests to an mqtt server.
MQTT_SERVER = '127.0.0.1'
#MQTT_USERNAME = ''
#MQTT_PASSWORD = ''
MQTT_TOPIC = '/voc/alert'
MQTT_MESSAGE = '{{"level":"info","component":"infobeamer-cms","msg":"{asset} uploaded by {user}. Check it at {url}"}}'

# Unix timestamp allows for specifying start/end time
# of uploaded content
TIME_MIN = 1640039559
Expand All @@ -46,6 +39,26 @@ TIME_MAX = 1640905200
# change this to invalidate cached static files.
VERSION = 1


# Push notifications for moderation requests. Supports MQTT with a
# c3voc-style notification client and [NTFY](https://ntfy.sh/).
[NOTIFIER]
# configure on which minute of the hour the hourly reminder of assets
# pending moderation gets sent. Set to -1 to disable.
#ALERT_MINUTE = 7

# configure mqtt alerts
#MQTT_SERVER = '127.0.0.1'
#MQTT_USERNAME = ''
#MQTT_PASSWORD = ''
#MQTT_TOPIC = '/voc/alert'

# configure alerts on ntfy
#NTFY = [
# "https://ntfy.example.com/infobeamer-cms",
#]


# extra assets. If this is empty, only the uploaded content and a notice
# on where to find the CMS will be shown. You can use this to overlay
# content on top of that content.
Expand Down
24 changes: 23 additions & 1 deletion syncer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import datetime
from json import dumps as json_dumps
from logging import getLogger

from conf import CONFIG
from helper import Asset, get_all_live_assets, user_is_admin
from helper import Asset, State, get_all_live_assets, get_assets, user_is_admin
from ib_hosted import ib
from notifier import Notifier

FADE_TIME = 0.5
SLIDE_TIME = 10
Expand Down Expand Up @@ -80,6 +82,26 @@ def asset_to_tiles(asset: Asset):
return tiles


if datetime.now().minute == int(CONFIG.get("NOTIFIER", {}).get("ALERT_MINUTE", 7)):
n = Notifier()
asset_states = {}
for asset in get_assets():
if asset.state not in (State.CONFIRMED, State.REJECTED, State.DELETED):
if asset.state not in asset_states:
asset_states[asset.state] = 0
asset_states[asset.state] += 1
log.info(
f"asset {asset.id} is in state {asset.state}, uploaded by {asset.user}"
)

msg = []
for state, count in sorted(asset_states.items()):
msg.append(f"{count} assets in state {state}.")

if msg:
n.message(" ".join(msg), level="WARN")


pages = []
assets_visible = set()
for asset in get_all_live_assets():
Expand Down

0 comments on commit d7f7067

Please sign in to comment.