Added display of stream name and tags to notifications.
This commit is contained in:
+52
-4
@@ -20,6 +20,9 @@ from urllib.parse import urlparse
|
|||||||
# Path to the GetStatus API call on Owncast instances
|
# Path to the GetStatus API call on Owncast instances
|
||||||
OWNCAST_STATUS_PATH = "/api/status"
|
OWNCAST_STATUS_PATH = "/api/status"
|
||||||
|
|
||||||
|
# Path to GetWebConfig API call on Owncast instances
|
||||||
|
OWNCAST_CONFIG_PATH = "/api/config"
|
||||||
|
|
||||||
# User agent to send with all HTTP requests.
|
# User agent to send with all HTTP requests.
|
||||||
USER_AGENT = "OwncastSentry/1.0.1 (bot; +https://git.logal.dev/LogalDeveloper/OwncastSentry)"
|
USER_AGENT = "OwncastSentry/1.0.1 (bot; +https://git.logal.dev/LogalDeveloper/OwncastSentry)"
|
||||||
|
|
||||||
@@ -108,7 +111,7 @@ class OwncastSentry(Plugin):
|
|||||||
if result[0] == 0:
|
if result[0] == 0:
|
||||||
# There are 0 subscriptions, we need to validate this domain is an Owncast stream.
|
# There are 0 subscriptions, we need to validate this domain is an Owncast stream.
|
||||||
# Attempt to fetch the stream state from this domain.
|
# Attempt to fetch the stream state from this domain.
|
||||||
stream_state = await self.get_current_stream_state(stream_domain)
|
stream_state = await self.get_stream_state(stream_domain)
|
||||||
if len(stream_state) == 0:
|
if len(stream_state) == 0:
|
||||||
# The stream state fetch returned nothing. Probably not an Owncast stream.
|
# The stream state fetch returned nothing. Probably not an Owncast stream.
|
||||||
await evt.reply("The URL you supplied does not appear to be a valid Owncast instance. You may have specified an invalid domain, or the instance is offline.")
|
await evt.reply("The URL you supplied does not appear to be a valid Owncast instance. You may have specified an invalid domain, or the instance is offline.")
|
||||||
@@ -221,7 +224,7 @@ class OwncastSentry(Plugin):
|
|||||||
send_notifications = True
|
send_notifications = True
|
||||||
|
|
||||||
# Fetch the latest stream state from the server.
|
# Fetch the latest stream state from the server.
|
||||||
new_state = await self.get_current_stream_state(domain)
|
new_state = await self.get_stream_state(domain)
|
||||||
|
|
||||||
# Skip the update if the fetch failed for any reason.
|
# Skip the update if the fetch failed for any reason.
|
||||||
if new_state == {}:
|
if new_state == {}:
|
||||||
@@ -281,14 +284,24 @@ class OwncastSentry(Plugin):
|
|||||||
successful_notifications = 0
|
successful_notifications = 0
|
||||||
failed_notifications = 0
|
failed_notifications = 0
|
||||||
|
|
||||||
|
stream_config = await self.get_stream_config(domain)
|
||||||
|
|
||||||
# Build the message body text.
|
# Build the message body text.
|
||||||
|
if "name" in stream_config:
|
||||||
|
body_text = "🎥 " + stream_config["name"] + " is now live!"
|
||||||
|
else:
|
||||||
|
# Turns out it is possible to set an empty name for an Owncast stream if you know how.
|
||||||
|
# We'll account for that... Just in case.
|
||||||
body_text = "🎥 " + domain + " is now live!"
|
body_text = "🎥 " + domain + " is now live!"
|
||||||
|
|
||||||
# Streams can have no title. If there is none, don't even mention it.
|
# Streams can have no title. If there is none, don't even mention it.
|
||||||
if title != "":
|
if title != "":
|
||||||
body_text += "\nStream Title: " + title
|
body_text += "\nStream Title: " + title
|
||||||
|
|
||||||
body_text += "\n\nTo tune in, visit: https://" + domain + "/"
|
body_text += "\n\nTo tune in, visit: https://" + domain + "/\n\n"
|
||||||
|
|
||||||
|
for tag in stream_config["tags"]:
|
||||||
|
body_text += "#" + tag + " "
|
||||||
|
|
||||||
# Iterate over the subscribed rooms and try to send a message to each.
|
# Iterate over the subscribed rooms and try to send a message to each.
|
||||||
# TODO: This should probably be made async.
|
# TODO: This should probably be made async.
|
||||||
@@ -311,7 +324,7 @@ class OwncastSentry(Plugin):
|
|||||||
|
|
||||||
|
|
||||||
# ========== HELPER METHODS ==========
|
# ========== HELPER METHODS ==========
|
||||||
async def get_current_stream_state(self, domain):
|
async def get_stream_state(self, domain):
|
||||||
"""
|
"""
|
||||||
Get the current stream state for a given domain.
|
Get the current stream state for a given domain.
|
||||||
HTTPS on port 443 is assumed, no other protocols or ports are supported.
|
HTTPS on port 443 is assumed, no other protocols or ports are supported.
|
||||||
@@ -360,6 +373,41 @@ class OwncastSentry(Plugin):
|
|||||||
|
|
||||||
return new_state
|
return new_state
|
||||||
|
|
||||||
|
async def get_stream_config(self, domain):
|
||||||
|
"""
|
||||||
|
Get the current stream config for a given domain.
|
||||||
|
HTTPS on port 443 is assumed, no other protocols or ports are supported.
|
||||||
|
|
||||||
|
:param domain: The domain (not URL) where the stream is hosted.
|
||||||
|
:return: A dictionary containing the stream's configuration.
|
||||||
|
"""
|
||||||
|
self.log.debug(f"[{domain}] Fetching current stream config...")
|
||||||
|
# Build a URL to the config API in Owncast. (https://owncast.online/api/latest/#tag/Internal/operation/GetWebConfig)
|
||||||
|
# Only use HTTPS, even if the user specified something else.
|
||||||
|
status_url = "https://" + domain + OWNCAST_CONFIG_PATH
|
||||||
|
|
||||||
|
# Make a request to the endpoint.
|
||||||
|
try:
|
||||||
|
response = await self.session.request("GET", status_url, allow_redirects=False)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(f"[{domain}] Error making GET request to {OWNCAST_STATUS_PATH}: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Check the response code is success
|
||||||
|
# TODO: Handle 429 rate limiting?
|
||||||
|
if response.status != 200:
|
||||||
|
self.log.warning(f"[{domain}] Response to request on {OWNCAST_STATUS_PATH} was not 200, got {response.status} instead.")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Try and interpret the response as JSON.
|
||||||
|
try:
|
||||||
|
config = json.loads(await response.read())
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning(f"[{domain}] Rejecting response to request on {OWNCAST_STATUS_PATH} as could not be interpreted as JSON: {e}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
def domainify(self, url) -> str:
|
def domainify(self, url) -> str:
|
||||||
"""
|
"""
|
||||||
Take a given URL and convert it to just the domain.
|
Take a given URL and convert it to just the domain.
|
||||||
|
|||||||
Reference in New Issue
Block a user