Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slack webhook notifications #80

Merged
merged 12 commits into from
Dec 4, 2024
12 changes: 9 additions & 3 deletions backend/schemas/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@ class TaskBase(SQLModel):
name: str = Field(max_length=50)
url: str = Field(sa_column=Column(URLType))
discord_url: str | None = Field(sa_column=Column(URLType), default=None)
slack_url: str | None = Field(sa_column=Column(URLType), default=None)
interval: int = Field(ge=MIN_INTERVAL_SECONDS, le=MAX_INTERVAL_SECONDS)
enabled_notification_options: NotificationOptions = Field(
default=["EMAIL"], sa_column=Column(JSON())
)
enabled: bool = True # If the task is enabled then it should be running

@validator("url", "discord_url")
@validator("url", "discord_url", "slack_url")
def validate_url(cls, value):
if value is None:
return value
Expand Down Expand Up @@ -106,7 +107,7 @@ def get_id(self):
return self.id

def notify(self, message: dict):
from utils.notifications import send_mail, send_discord_msg
from utils.notifications import send_mail, send_discord_msg, send_slack_msg

subject = message["subject"]
body = message["body"]
Expand Down Expand Up @@ -134,6 +135,10 @@ def notify(self, message: dict):
pass
if "SLACK" in self.enabled_notification_options:
# send slack message
try:
send_slack_msg(self.slack_url, body)
except Exception as e:
logging.error(f"Failed to send slack message: {e}")
pass

def scan(self):
Expand Down Expand Up @@ -216,6 +221,7 @@ class TaskUpdate(TaskBase):
name: str | None = Field(default=None, max_length=50)
url: str | None = None
discord_url: str | None = None
slack_url: str | None = None
interval: int | None = Field(
default=None, ge=MIN_INTERVAL_SECONDS, le=MAX_INTERVAL_SECONDS
)
Expand All @@ -224,7 +230,7 @@ class TaskUpdate(TaskBase):
)
enabled: bool | None = None

@validator("url", "discord_url", pre=True, always=True)
@validator("url", "discord_url", "slack_url", pre=True, always=True)
def validate_url(cls, value):
if value is None or value == "":
return value
Expand Down
32 changes: 31 additions & 1 deletion backend/utils/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def send_mail(subject: str, message: str, recipients: list[str]):
server.quit()


def send_discord_msg(webhook_url, message, retries=5):
def send_discord_msg(webhook_url: str, message: str, retries: int = 5):
headers = {"Content-Type": "application/json"}
while message:
chunk = message[:2048]
Expand Down Expand Up @@ -76,6 +76,36 @@ def send_discord_msg(webhook_url, message, retries=5):
raise


def send_slack_msg(webhook_url: str, message: str, retries: int = 5):
headers = {"Content-Type": "application/json"}
while message:
chunk = message[
:3000
] # Slack's block text limit is higher than Discord's character limit
message = message[3000:]
payload = {
"text": chunk,
"username": "WebWatch",
"icon_emoji": ":globe_with_meridians:",
}

for attempt in range(retries):
try:
response = requests.post(
webhook_url, data=json.dumps(payload), headers=headers
)
response.raise_for_status()
print("Slack message sent successfully.")
break
except requests.exceptions.RequestException as e:
if attempt < retries - 1:
print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
time.sleep(1.1**attempt) # Exponential backoff
else:
print(f"All {retries} attempts failed: {e}")
raise


def send_password_reset_email(recipient_email: str, reset_link: str):
reset_link = str(reset_link)
load_dotenv()
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/EditTaskModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const EditTaskModal: EditTaskModalComponent = ({ task, closeModal }) => {
url: task.url,
notificationOptions: task.enabledNotificationOptions,
discordUrl: task.discordUrl || "",
slackUrl: "",
slackUrl: task.slackUrl || "",
interval: task.interval,
errors: {},
});
Expand Down Expand Up @@ -133,7 +133,7 @@ const EditTaskModal: EditTaskModalComponent = ({ task, closeModal }) => {
onClick={closeModal}
>
<form
className="relative mx-3 max-h-[90%] w-[1000px] overflow-scroll rounded-lg bg-primary p-8
className="relative mx-3 max-h-[90%] w-[1000px] overflow-auto rounded-lg bg-primary p-8
xxl:w-[1200px] xxl:p-10"
onSubmit={handleFormSubmit}
onClick={(e) => e.stopPropagation()}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ const Settings = () => {
className="mb-4 w-full rounded-lg border border-border bg-secondary p-2 outline-none"
onChange={handlePasswordInputEvents}
/>
<label className="mb-2 block xxl:text-lg">Confirm New Password</label>
<label className="mb-2 block xxl:text-lg">
Confirm New Password
</label>
<input
name="password-reset-confirm-password"
type="password"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/pages/Tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const Tasks = () => {
content: data.content,
url: data.url,
discordUrl: data.discord_url,
slackUrl: data.slack_url,
interval: data.interval,
enabledNotificationOptions: data.enabled_notification_options,
enabled: data.enabled,
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface TaskResponse {
content?: string;
url: string;
discord_url?: string;
slack_url?: string;
interval: number;
enabled_notification_options: string[];
enabled: boolean;
Expand All @@ -16,6 +17,7 @@ export interface Task {
content?: string;
url: string;
discordUrl?: string;
slackUrl?: string;
interval: number;
enabledNotificationOptions: string[];
enabled: boolean;
Expand Down
Loading