Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4c68447
fixed
Yashgupta9330 Mar 6, 2025
5aebd16
Merge branch 'main' into sync
Yashgupta9330 Mar 7, 2025
1abba39
Merge branch 'OWASP:main' into sync
Yashgupta9330 Mar 7, 2025
cc0e09e
fixed linting error
Yashgupta9330 Mar 7, 2025
d1b1da7
Merge branch 'main' into sync
Yashgupta9330 Mar 7, 2025
1d78412
Merge branch 'main' into sync
Yashgupta9330 Mar 8, 2025
7cf6528
did some minor changes
Yashgupta9330 Mar 8, 2025
67e4ad0
Merge branch 'main' into sync
Yashgupta9330 Mar 8, 2025
7fb112f
pulled
Yashgupta9330 Mar 8, 2025
342b359
pulled
Yashgupta9330 Mar 8, 2025
28ab7dc
Merge branch 'main' into sync
Yashgupta9330 Mar 9, 2025
92a1ca4
deleted some migrations
Yashgupta9330 Mar 9, 2025
f9bbf17
pulled
Yashgupta9330 Mar 9, 2025
c27f386
Merge branch 'main' into sync
arkid15r Mar 11, 2025
3782aaf
Merge branch 'main' into sync
Yashgupta9330 Mar 12, 2025
c649a50
Refactor Slack conversation model and add sync command for Slack chan…
Yashgupta9330 Mar 12, 2025
4097bbd
Merge branch 'main' into sync
Yashgupta9330 Mar 12, 2025
a93b748
Merge branch 'main' into sync
Yashgupta9330 Mar 12, 2025
1424246
Merge branch 'main' into sync
Yashgupta9330 Mar 13, 2025
88963a9
did minor changes
Yashgupta9330 Mar 13, 2025
56d840b
Merge branch 'main' into sync
Yashgupta9330 Mar 13, 2025
aeb6f7d
Merge branch 'main' into sync
Yashgupta9330 Mar 13, 2025
55c04e8
Merge branch 'main' into sync
Yashgupta9330 Mar 13, 2025
cda007b
Merge branch 'main' into sync
Yashgupta9330 Mar 13, 2025
5dab8eb
Merge branch 'main' into sync
Yashgupta9330 Mar 14, 2025
3b96cfa
Merge branch 'main' into sync
Yashgupta9330 Mar 15, 2025
90ae352
Merge branch 'OWASP:main' into sync
Yashgupta9330 Mar 16, 2025
c5ee59d
Merge branch 'main' into sync
Yashgupta9330 Mar 17, 2025
8176ba7
Merge branch 'main' into pr/Yashgupta9330/1032
arkid15r Mar 17, 2025
bc0857d
Update code
arkid15r Mar 17, 2025
0d65b9e
Merge branch 'main' into sync
Yashgupta9330 Mar 18, 2025
6bd79a0
Merge branch 'main' into sync
Yashgupta9330 Mar 18, 2025
62cbe49
added test
Yashgupta9330 Mar 18, 2025
421aa7f
Merge branch 'main' into sync
Yashgupta9330 Mar 19, 2025
292a32f
Merge branch 'main' into sync
Yashgupta9330 Mar 26, 2025
b9bcfe2
Merge branch 'main' into sync
Yashgupta9330 Mar 26, 2025
016ecfe
Merge branch 'main' into sync
Yashgupta9330 Mar 30, 2025
be5576f
Merge branch 'main' into sync
Yashgupta9330 Mar 31, 2025
5079694
Merge branch 'main' into sync
Yashgupta9330 Apr 1, 2025
c0112e9
Merge branch 'main' into sync
Yashgupta9330 Apr 8, 2025
c347926
Merge branch 'main' into sync
Yashgupta9330 Apr 18, 2025
703acc3
added
Yashgupta9330 Apr 18, 2025
c7eae97
Merge branch 'main' into sync
Yashgupta9330 Apr 20, 2025
c2936c8
Merge branch 'main' into sync
Yashgupta9330 Apr 20, 2025
77e96cc
Merge branch 'main' into sync
Yashgupta9330 Apr 20, 2025
be4fe5d
Merge branch 'main' into pr/Yashgupta9330/1032
arkid15r May 12, 2025
97c1316
Update code
arkid15r May 12, 2025
a2765e1
Merge channels/conversations
arkid15r May 13, 2025
c4f1329
Merge branch 'main' into pr/Yashgupta9330/1032
arkid15r May 13, 2025
92dc7e8
Update code
arkid15r May 13, 2025
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
4 changes: 4 additions & 0 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ owasp-process-snapshots:
@echo "Processing OWASP snapshots"
@CMD="python manage.py owasp_process_snapshots" $(MAKE) exec-backend-command

slack-sync-conversation-list:
Comment thread
Yashgupta9330 marked this conversation as resolved.
Outdated
@echo "Syncing Slack conversation list"
@CMD="python manage.py slack_sync_conversation_list" $(MAKE) exec-backend-command

owasp-update-project-health-metrics:
@echo "Updating OWASP project health requirements"
@CMD="python manage.py owasp_update_project_health_metrics" $(MAKE) exec-backend-command
Expand Down
43 changes: 43 additions & 0 deletions backend/apps/owasp/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from apps.owasp.models.chapter import Chapter
from apps.owasp.models.committee import Committee
from apps.owasp.models.conversation import Conversation
from apps.owasp.models.event import Event
from apps.owasp.models.project import Project
from apps.owasp.models.project_health_metrics import ProjectHealthMetrics
Expand Down Expand Up @@ -172,6 +173,47 @@ class SponsorAdmin(admin.ModelAdmin):
)


class ConversationAdmin(admin.ModelAdmin):
"""Admin configuration for Conversation model."""

list_display = ("name", "entity_id", "created_at", "is_private", "is_archived", "is_general")
search_fields = ("name", "topic", "purpose", "entity_id", "creator_id")
list_filter = ("created_at", "is_private", "is_archived", "is_general")
readonly_fields = ("entity_id", "created_at", "creator_id")
fieldsets = (
(
"Conversation Information",
{
"fields": (
"entity_id",
"name",
"created_at",
"creator_id",
)
},
),
(
"Properties",
{
"fields": (
"is_private",
"is_archived",
"is_general",
)
},
),
(
"Content",
{
"fields": (
"topic",
"purpose",
)
},
),
)


admin.site.register(Chapter, ChapterAdmin)
admin.site.register(Committee, CommitteeAdmin)
admin.site.register(Event, EventAdmin)
Expand All @@ -180,3 +222,4 @@ class SponsorAdmin(admin.ModelAdmin):
admin.site.register(ProjectHealthRequirements)
admin.site.register(Snapshot, SnapshotAdmin)
admin.site.register(Sponsor, SponsorAdmin)
admin.site.register(Conversation, ConversationAdmin)
137 changes: 137 additions & 0 deletions backend/apps/owasp/management/commands/slack_sync_conversation_list.py
Comment thread
Yashgupta9330 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Command to sync Slack channels and groups from the OWASP Slack workspace."""

import logging
import time
from datetime import datetime, timezone

import requests
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction

logger = logging.getLogger(__name__)


class SlackSyncError(Exception):
"""Custom exception for Slack sync errors."""

MISSING_TOKEN_ERROR = "SLACK BOT TOKEN not found in settings" # noqa: S105
API_ERROR_FORMAT = "Slack API error: {}"


class Command(BaseCommand):
help = "Sync Slack channels and groups from the OWASP Slack workspace."

def add_arguments(self, parser):
parser.add_argument(
"--batch-size",
type=int,
default=200,
help="Number of conversations to retrieve per request",
)
parser.add_argument(
"--delay", type=float, default=0.5, help="Delay between API requests in seconds"
)

def handle(self, *args, **options):
from apps.owasp.models.conversation import Conversation

try:
batch_size = options["batch_size"]
delay = options["delay"]

count = self.sync_slack_conversations(Conversation, batch_size=batch_size, delay=delay)

self.stdout.write(
self.style.SUCCESS(f"Successfully synced {count} Slack conversations")
)
except Exception as e:
error_msg = f"Failed to sync Slack conversations: {e}"
logger.exception(error_msg)
self.stdout.write(self.style.ERROR(error_msg))
raise

def sync_slack_conversations(self, conversation_model, batch_size=200, delay=0.5):
slack_token = getattr(settings, "SLACK_BOT_TOKEN", None)
if not slack_token:
raise SlackSyncError(SlackSyncError.MISSING_TOKEN_ERROR)

url = "https://slack.com/api/conversations.list"
Comment thread
Yashgupta9330 marked this conversation as resolved.
Outdated
params = {
"limit": batch_size,
"exclude_archived": False,
"types": "public_channel,private_channel",
}
headers = {"Authorization": f"Bearer {slack_token}"}

next_cursor = None
total_processed = 0

while True:
if next_cursor:
params["cursor"] = next_cursor

response = requests.get(url, headers=headers, params=params, timeout=10)
data = response.json()

if not data.get("ok"):
error_msg = f"Slack API error: {data.get('error', 'Unknown error')}"
logger.error(error_msg)
raise SlackSyncError(error_msg)

conversations = data.get("channels", [])
self.process_conversations(conversations, conversation_model)
total_processed += len(conversations)

self.stdout.write(f"Processed {len(conversations)} conversations...")

next_cursor = data.get("response_metadata", {}).get("next_cursor")
if not next_cursor:
break

# Add a small delay to avoid rate limiting
time.sleep(delay)

return total_processed

@transaction.atomic
def process_conversations(self, conversations, conversation_model):
conversation_objects = []

for conversation in conversations:
try:
# Convert Unix timestamp to datetime
created_timestamp = int(conversation.get("created", 0))
created_datetime = datetime.fromtimestamp(created_timestamp, tz=timezone.utc)

defaults = {
"name": conversation.get("name", ""),
"created_at": created_datetime,
"is_private": conversation.get("is_private", False),
"is_archived": conversation.get("is_archived", False),
"is_general": conversation.get("is_general", False),
"topic": conversation.get("topic", {}).get("value", ""),
"purpose": conversation.get("purpose", {}).get("value", ""),
"creator_id": conversation.get("creator", ""),
}

obj, created = conversation_model.objects.update_or_create(
entity_id=conversation["id"], defaults=defaults
)
conversation_objects.append(obj)

if created:
logger.info("Created new conversation: %s", obj.name)
else:
logger.info("Updated conversation: %s", obj.name)

except KeyError:
logger.exception("Missing required field in conversation data")
continue
except Exception:
logger.exception(
"Error processing conversation %s", conversation.get("id", "unknown")
)
continue

return conversation_objects
37 changes: 37 additions & 0 deletions backend/apps/owasp/migrations/0023_conversation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Generated by Django 5.1.7 on 2025-03-07 12:22

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("owasp", "0022_sponsor"),
]

operations = [
migrations.CreateModel(
name="Conversation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("entity_id", models.CharField(max_length=255, unique=True)),
("name", models.CharField(max_length=255)),
("created_at", models.DateTimeField()),
("is_private", models.BooleanField(default=False)),
("is_archived", models.BooleanField(default=False)),
("is_general", models.BooleanField(default=False)),
("topic", models.TextField(blank=True, default="")),
("purpose", models.TextField(blank=True, default="")),
("creator_id", models.CharField(max_length=255)),
("updated_at", models.CharField(max_length=255)),
],
options={
"verbose_name_plural": "Conversations",
"db_table": "slack_conversations",
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Generated by Django 5.1.7 on 2025-03-07 13:25

import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("owasp", "0023_conversation"),
]

operations = [
migrations.RemoveField(
model_name="conversation",
name="updated_at",
),
migrations.AddField(
model_name="conversation",
name="nest_created_at",
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name="conversation",
name="nest_updated_at",
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name="conversation",
name="created_at",
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterModelTable(
name="conversation",
table="owasp_slack_conversations",
),
]
27 changes: 27 additions & 0 deletions backend/apps/owasp/models/conversation.py
Comment thread
Yashgupta9330 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""OWASP app conversation models."""

from django.db import models

from apps.common.models import TimestampedModel


class Conversation(TimestampedModel):
"""Model representing a Slack conversation (channel or group)."""

class Meta:
db_table = "owasp_slack_conversations"
Comment thread
Yashgupta9330 marked this conversation as resolved.
Outdated
verbose_name_plural = "Conversations"

entity_id = models.CharField(max_length=255, unique=True)
name = models.CharField(max_length=255)
created_at = models.DateTimeField(blank=True, null=True)
is_private = models.BooleanField(default=False)
is_archived = models.BooleanField(default=False)
is_general = models.BooleanField(default=False)
topic = models.TextField(blank=True, default="")
purpose = models.TextField(blank=True, default="")
creator_id = models.CharField(max_length=255)

def __str__(self):
"""Return a string representation of the conversation."""
return self.name