Skip to content
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
54 changes: 41 additions & 13 deletions endpoints/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import json
import logging # Ensure logging is imported
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove unnecessary comments like # Ensure logging is imported

from typing import Literal, Mapping, Optional
from werkzeug import Request, Response
from middlewares.discord_middleware import DiscordMiddleware
from middlewares.folo_middleware import FoloMiddleware # Import FoloMiddleware
from middlewares.default_middleware import DefaultMiddleware

logger = logging.getLogger(__name__) # Ensure logger is initialized

def apply_middleware(r: Request, settings: Mapping) -> Optional[Response]:
"""
Applies middleware based on the settings provided.
Expand All @@ -12,27 +16,51 @@ def apply_middleware(r: Request, settings: Mapping) -> Optional[Response]:
:param settings: A dictionary containing configuration settings
:return: A Response object if middleware processing returns a response, otherwise None
"""
middleware_response: Optional[Response] = None
middleware_processed_request = False # Flag to see if a specific middleware modified the request

try:
middleware_type = settings.get("middleware")
signature_verification_key = settings.get("signature_verification_key")

if middleware_type == "discord":
logger.debug("Applying Discord middleware")
middleware = DiscordMiddleware(signature_verification_key)
response = middleware.invoke(r)
if response:
return response
except (json.JSONDecodeError, KeyError, TypeError) as e:
print(f"Middleware Error: {str(e)}")
return Response(json.dumps({"error": f"Middleware error: {str(e)}"}), status=500, content_type="application/json")
middleware_response = middleware.invoke(r)
if hasattr(r, 'default_middleware_json'): # Check if DiscordMiddleware modified the request
middleware_processed_request = True
elif middleware_type == "folo":
logger.debug("Applying Folo middleware")
middleware = FoloMiddleware() # Instantiate FoloMiddleware
middleware_response = middleware.invoke(r) # Call its invoke method
if hasattr(r, 'default_middleware_json'): # Check if FoloMiddleware modified the request
middleware_processed_request = True

if middleware_response: # If any middleware returned a direct response (e.g. on error)
return middleware_response

try:
default_middleware = DefaultMiddleware()
default_middleware.invoke(r, settings)
except (json.JSONDecodeError, KeyError, TypeError) as e:
print(f"Default Middleware Error: {str(e)}")
return Response(json.dumps({"error": f"Default Middleware error: {str(e)}"}), status=500, content_type="application/json")
except Exception as e: # Catch errors during specific middleware instantiation or invocation
logger.error(f"Error during {middleware_type} middleware processing: {str(e)}", exc_info=True)
return Response(json.dumps({"error": f"Error in {middleware_type} middleware: {str(e)}"}),
status=500, content_type="application/json")

return None
# Apply DefaultMiddleware only if no other middleware has processed the request
# and no specific middleware returned an error response.
if not middleware_processed_request and not middleware_response:
try:
logger.debug("Applying Default middleware")
default_middleware = DefaultMiddleware()
# Assuming DefaultMiddleware.invoke might also set r.default_middleware_json or return a Response
# It should also return None if it just modifies r.default_middleware_json
default_middleware_response = default_middleware.invoke(r, settings)
if default_middleware_response:
return default_middleware_response
except Exception as e:
logger.error(f"Default Middleware Error: {str(e)}", exc_info=True)
return Response(json.dumps({"error": f"Default Middleware error: {str(e)}"}),
status=500, content_type="application/json")

return None # If all middlewares passed (returned None) and did not error

def validate_api_key(r: Request, settings: Mapping) -> Optional[Response]:
"""
Expand Down
5 changes: 5 additions & 0 deletions group/webhook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ settings:
en_US: Discord
zh_Hans: Discord
pt_BR: Discord
- value: folo
label:
en_US: Folo
zh_Hans: Folo
pt_BR: Folo
- value: none
label:
en_US: None
Expand Down
2 changes: 1 addition & 1 deletion manifest.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 0.5.1
version: 0.5.2
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new feature, so new version should be 0.6.0

type: plugin
author: perzeuss
name: webhook
Expand Down
87 changes: 87 additions & 0 deletions middlewares/folo_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import json
import logging
from werkzeug import Request, Response
from typing import Optional

logger = logging.getLogger(__name__)

class FoloMiddleware:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create a test for this middleware like in tests/middlewares/test_discord_middleware.py

def __init__(self):
"""
Initializes the FoloMiddleware.
Currently, it does not require specific settings from the main config.
"""
pass

def invoke(self, r: Request) -> Optional[Response]:
"""
Processes the request if it's a Folo webhook.
- Parses the Folo payload.
- Transforms it into the Dify-expected format with query="start"
and specific fields mapped to inputs.
- Stores the transformed payload in r.default_middleware_json.
Returns:
- A Response object if an error occurs during processing (e.g., invalid payload).
- None if the transformation is successful, allowing the main handler to proceed
using r.default_middleware_json.
"""
logger.debug("FoloMiddleware: Processing request...")
try:
folo_payload = r.get_json()
if not folo_payload or "entry" not in folo_payload:
logger.error("FoloMiddleware: Invalid or missing 'entry' in Folo payload.")
return Response(
json.dumps({"error": "Folo middleware: Invalid payload, 'entry' field is missing."}),
status=400,
content_type="application/json"
)

entry = folo_payload.get("entry", {})

# Extract required fields from the Folo 'entry' object
title = entry.get("title")
content = entry.get("content")
author = entry.get("author")
url = entry.get("url")
published_at = entry.get("publishedAt")
description = entry.get("description")

# Construct the new payload for Dify
dify_inputs = {
"title": title,
"content": content,
"author": author,
"url": url,
"publishedAt": published_at,
"description": description
}

# Add debug logging for extracted inputs
logger.debug(f"FoloMiddleware: Extracted inputs for Dify: {dify_inputs}")

dify_payload = {
"query": "start",
"inputs": dify_inputs
}

# Store the transformed payload in the request object for the main endpoint handler
r.default_middleware_json = dify_payload
logger.info("FoloMiddleware: Payload transformed successfully and stored in r.default_middleware_json.")

return None # Indicate successful transformation

except json.JSONDecodeError as e:
logger.error(f"FoloMiddleware: JSONDecodeError while parsing Folo payload - {str(e)}")
return Response(
json.dumps({"error": f"Folo middleware: Invalid JSON payload - {str(e)}"}),
status=400,
content_type="application/json"
)
except Exception as e:
logger.error(f"FoloMiddleware: Unexpected error during processing - {str(e)}", exc_info=True)
return Response(
json.dumps({"error": f"Folo middleware: Unexpected error - {str(e)}"}),
status=500,
content_type="application/json"
)
Loading