Skip to content

Commit

Permalink
Addition of Contribute Section in Home of Slack NestBot (OWASP#593)
Browse files Browse the repository at this point in the history
* Addition of Contribute Section in Home of Slack NestBot

* Update contribute.py

* Update of apps/slack/commands/contribute

* Minor changes

* Update of code after the review

* Update backend/apps/slack/commands/contribute.py

Co-authored-by: Nitin Awari <[email protected]>

* Update urls.py

* Update contribute.py

* Update code

---------

Co-authored-by: Nitin Awari <[email protected]>
Co-authored-by: Arkadii Yakovets <[email protected]>
Co-authored-by: Arkadii Yakovets <[email protected]>
  • Loading branch information
4 people authored Feb 3, 2025
1 parent 1bc2529 commit 62646d9
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 213 deletions.
15 changes: 14 additions & 1 deletion backend/apps/slack/actions/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from apps.slack.apps import SlackConfig
from apps.slack.blocks import get_header, markdown
from apps.slack.common.handlers import chapters, committees, projects
from apps.slack.common.handlers import chapters, committees, contribute, projects
from apps.slack.common.presentation import EntityPresentation
from apps.slack.constants import (
VIEW_CHAPTERS_ACTION,
Expand All @@ -15,6 +15,9 @@
VIEW_COMMITTEES_ACTION,
VIEW_COMMITTEES_ACTION_NEXT,
VIEW_COMMITTEES_ACTION_PREV,
VIEW_CONTRIBUTE_ACTION,
VIEW_CONTRIBUTE_ACTION_NEXT,
VIEW_CONTRIBUTE_ACTION_PREV,
VIEW_PROJECTS_ACTION,
VIEW_PROJECTS_ACTION_NEXT,
VIEW_PROJECTS_ACTION_PREV,
Expand Down Expand Up @@ -67,6 +70,13 @@ def handle_home_actions(ack, body, client):
VIEW_PROJECTS_ACTION_NEXT,
}:
blocks = projects.get_blocks(page=page, limit=10, presentation=home_presentation)

case action if action in {
VIEW_CONTRIBUTE_ACTION,
VIEW_CONTRIBUTE_ACTION_PREV,
VIEW_CONTRIBUTE_ACTION_NEXT,
}:
blocks = contribute.get_blocks(page=page, limit=10, presentation=home_presentation)
case _:
blocks = [markdown("Invalid action, please try again.")]

Expand All @@ -93,6 +103,9 @@ def handle_home_actions(ack, body, client):
VIEW_COMMITTEES_ACTION_NEXT,
VIEW_COMMITTEES_ACTION_PREV,
VIEW_COMMITTEES_ACTION,
VIEW_CONTRIBUTE_ACTION_NEXT,
VIEW_CONTRIBUTE_ACTION_PREV,
VIEW_CONTRIBUTE_ACTION,
VIEW_PROJECTS_ACTION_NEXT,
VIEW_PROJECTS_ACTION_PREV,
VIEW_PROJECTS_ACTION,
Expand Down
10 changes: 10 additions & 0 deletions backend/apps/slack/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ def get_header():
"value": "view_committees",
"action_id": "view_committees_action",
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Contribute",
"emoji": True,
},
"value": "view_contribute",
"action_id": "view_contribute_action",
},
],
},
]
Expand Down
86 changes: 22 additions & 64 deletions backend/apps/slack/commands/contribute.py
Original file line number Diff line number Diff line change
@@ -1,89 +1,47 @@
"""Slack bot contribute command."""

from django.conf import settings
from django.utils.text import Truncator

from apps.common.constants import NL
from apps.common.utils import get_absolute_url
from apps.slack.apps import SlackConfig
from apps.slack.blocks import markdown
from apps.slack.common.constants import COMMAND_START
from apps.slack.constants import FEEDBACK_CHANNEL_MESSAGE
from apps.slack.utils import escape
from apps.slack.common.constants import COMMAND_HELP, COMMAND_START
from apps.slack.common.handlers.contribute import get_blocks
from apps.slack.common.presentation import EntityPresentation

COMMAND = "/contribute"
SUMMARY_TRUNCATION_LIMIT = 255
TITLE_TRUNCATION_LIMIT = 80


def contribute_handler(ack, command, client):
"""Slack /contribute command handler."""
from apps.github.models.issue import Issue
from apps.owasp.api.search.issue import get_issues
from apps.slack.common.contribute import CONTRIBUTE_GENERAL_INFORMATION_BLOCKS

ack()
if not settings.SLACK_COMMANDS_ENABLED:
return

command_text = command["text"].strip()
if command_text in COMMAND_START:

if command_text in COMMAND_HELP:
blocks = [
*CONTRIBUTE_GENERAL_INFORMATION_BLOCKS,
markdown(f"{FEEDBACK_CHANNEL_MESSAGE}"),
markdown(
f"*Available Commands for Contributing:*{NL}"
f"•`/contribute` - View all available issues.{NL}"
f"•`/contribute <search term>` - Search for contribution opportunities.{NL}"
),
]
else:
search_query_escaped = escape(command_text)
blocks = [
markdown(f"*No results found for `{COMMAND} {search_query_escaped}`*{NL}"),
]

attributes = [
"idx_project_name",
"idx_summary",
"idx_title",
"idx_url",
]
if issues := get_issues(
command_text,
attributes=attributes,
distinct=not command_text,
search_query = "" if command_text in COMMAND_START else command_text
blocks = get_blocks(
search_query=search_query,
limit=10,
)["hits"]:
blocks = [
markdown(
(
f"{NL}*Here are top 10 most relevant issues "
f"that I found based on *{NL} `{COMMAND} {search_query_escaped}`:{NL}"
)
if search_query_escaped
else (f"{NL}*Here are top 10 most recent issues:*{NL}")
),
]

for idx, issue in enumerate(issues):
title_truncated = Truncator(escape(issue["idx_title"])).chars(
TITLE_TRUNCATION_LIMIT, truncate="..."
)
summary_truncated = Truncator(issue["idx_summary"]).chars(
SUMMARY_TRUNCATION_LIMIT, truncate="..."
)
blocks.append(
markdown(
f"{NL}*{idx + 1}.* <{issue['idx_url']}|*{title_truncated}*>{NL}"
f"{escape(issue['idx_project_name'])}{NL}"
f"{escape(summary_truncated)}{NL}"
),
)

blocks.append(
markdown(
f"⚠️ *Extended search over {Issue.open_issues_count()} open issues "
f"is available at <{get_absolute_url('projects/contribute')}"
f"?q={command_text}|{settings.SITE_NAME}>*\n"
f"{FEEDBACK_CHANNEL_MESSAGE}"
),
)
presentation=EntityPresentation(
include_feedback=True,
include_metadata=True,
include_pagination=False,
include_timestamps=True,
name_truncation=80,
summary_truncation=300,
),
)

conversation = client.conversations_open(users=command["user_id"])
client.chat_postMessage(channel=conversation["channel"]["id"], blocks=blocks)
Expand Down
87 changes: 87 additions & 0 deletions backend/apps/slack/common/handlers/contribute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Handler for OWASP Contribute Slack functionality."""

from __future__ import annotations

from django.utils.text import Truncator

from apps.common.constants import NL
from apps.slack.blocks import get_pagination_buttons, markdown
from apps.slack.common.constants import TRUNCATION_INDICATOR
from apps.slack.common.presentation import EntityPresentation
from apps.slack.constants import FEEDBACK_CHANNEL_MESSAGE
from apps.slack.utils import escape


def get_blocks(
page=1, search_query: str = "", limit: int = 10, presentation: EntityPresentation | None = None
):
"""Get contribute blocks."""
from apps.github.models.issue import Issue
from apps.owasp.api.search.issue import get_issues

presentation = presentation or EntityPresentation()
search_query_escaped = escape(search_query)

attributes = [
"idx_project_name",
"idx_project_url",
"idx_summary",
"idx_title",
"idx_url",
]

offset = (page - 1) * limit
contribute_data = get_issues(search_query, attributes=attributes, limit=limit, page=page)
issues = contribute_data["hits"]
total_pages = contribute_data["nbPages"]

if not issues:
return [
markdown(
f"*No issues found for `{search_query_escaped}`*{NL}"
if search_query
else "*No issues found*{NL}"
)
]

blocks = []
for idx, issue in enumerate(issues):
title = Truncator(escape(issue["idx_title"])).chars(
presentation.name_truncation, truncate=TRUNCATION_INDICATOR
)
project_name = escape(issue["idx_project_name"])
project_url = escape(issue["idx_project_url"])
summary = Truncator(escape(issue["idx_summary"])).chars(
presentation.summary_truncation, truncate=TRUNCATION_INDICATOR
)

blocks.append(
markdown(
f"{offset + idx + 1}. <{issue['idx_url']}|*{title}*>{NL}"
f"<{project_url}|{project_name}>{NL}"
f"{summary}{NL}"
)
)

if presentation.include_feedback:
blocks.append(
markdown(
f"Extended search over {Issue.open_issues_count()} OWASP issues"
f"{FEEDBACK_CHANNEL_MESSAGE}"
)
)
if presentation.include_pagination and (
pagination_block := get_pagination_buttons(
"contribute",
page,
total_pages - 1,
)
):
blocks.append(
{
"type": "actions",
"elements": pagination_block,
}
)

return blocks
2 changes: 1 addition & 1 deletion backend/apps/slack/common/presentation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""default entities presentation for chapters, committees, project,."""
"""default entities presentation for OWASP entities (project, events, etc)."""

from dataclasses import dataclass

Expand Down
4 changes: 4 additions & 0 deletions backend/apps/slack/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
VIEW_CHAPTERS_ACTION_NEXT = "view_chapters_action_next"
VIEW_CHAPTERS_ACTION_PREV = "view_chapters_action_prev"

VIEW_CONTRIBUTE_ACTION = "view_contribute_action"
VIEW_CONTRIBUTE_ACTION_NEXT = "view_contribute_action_next"
VIEW_CONTRIBUTE_ACTION_PREV = "view_contribute_action_prev"


FEEDBACK_CHANNEL_MESSAGE = (
f"💬 You can share feedback on your {NEST_BOT_NAME} experience "
Expand Down
Loading

0 comments on commit 62646d9

Please sign in to comment.