Skip to content

Commit 37f4c66

Browse files
feat: AI integratoin for bacon scores with a fallback mechanism if API calls are not working (#4398)
* organisation UI changed * profile integrated * added UI for bacon in issues and bug * integrated with reporting Ips * precommit fixes * fix recurring bug * ai integrated * fix: logging removed * linting fixed --------- Co-authored-by: DonnieBLT <[email protected]>
1 parent c393829 commit 37f4c66

File tree

3 files changed

+165
-18
lines changed

3 files changed

+165
-18
lines changed

website/feed_signals.py

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,30 @@
1818
UserBadge,
1919
UserProfile,
2020
)
21+
from .utils import analyze_contribution
22+
23+
# Default BACON rewards for different contribution types
24+
DEFAULT_BACON_REWARDS = {
25+
"Issue": 5,
26+
"Post": 10,
27+
"Hunt": 15,
28+
"IpReport": 3,
29+
"Organization": 10,
30+
"ForumPost": 2,
31+
"Bid": 2,
32+
}
33+
34+
35+
def get_default_bacon_reward(instance, action_type):
36+
"""Get the default BACON reward for a given instance type"""
37+
model_name = instance._meta.model_name.capitalize()
38+
base_reward = DEFAULT_BACON_REWARDS.get(model_name, 1)
39+
40+
# Add bonus for security-related content if applicable
41+
if hasattr(instance, "is_security") and getattr(instance, "is_security", False):
42+
base_reward += 3
43+
44+
return base_reward
2145

2246

2347
def get_default_user():
@@ -62,37 +86,70 @@ def create_activity(instance, action_type):
6286
)
6387

6488

65-
def giveBacon(user, amt=1):
66-
# Check if the user already has a token record
89+
def giveBacon(user, instance=None, action_type=None, amt=None):
90+
"""
91+
Award BACON tokens to a user based on their contribution.
92+
If amt is provided, use that amount directly.
93+
Otherwise, analyze the contribution using AI to determine the amount.
94+
Falls back to default rewards if AI analysis fails.
95+
"""
6796
if user is None or user.is_authenticated is False:
6897
return
69-
token_earning, created = BaconEarning.objects.get_or_create(user=user)
7098

71-
if created:
72-
token_earning.tokens_earned = amt # Reward 10 tokens for completing the action (adjust as needed)
73-
else:
74-
token_earning.tokens_earned += amt # Add 10 tokens if the user already exists in the system
99+
token_earning, created = BaconEarning.objects.get_or_create(user=user)
75100

76-
token_earning.save() # Save the updated record
101+
try:
102+
# If amount is specified, use it
103+
if amt is not None:
104+
reward_amount = amt
105+
# Otherwise analyze the contribution if we have an instance
106+
elif instance is not None and action_type is not None:
107+
try:
108+
reward_amount = analyze_contribution(instance, action_type)
109+
except Exception as e:
110+
# Fallback to default reward system
111+
reward_amount = get_default_bacon_reward(instance, action_type)
112+
else:
113+
reward_amount = 1 # Default minimum reward
114+
115+
# Ensure reward amount is within valid range (1-50)
116+
reward_amount = max(1, min(50, int(reward_amount)))
117+
118+
if created:
119+
token_earning.tokens_earned = reward_amount
120+
else:
121+
token_earning.tokens_earned += reward_amount
122+
123+
token_earning.save()
124+
return reward_amount
125+
126+
except Exception as e:
127+
# If anything fails, ensure at least minimum reward is given
128+
if created:
129+
token_earning.tokens_earned = 1
130+
else:
131+
token_earning.tokens_earned += 1
132+
token_earning.save()
133+
return 1
77134

78135

79136
@receiver(post_save)
80137
def handle_post_save(sender, instance, created, **kwargs):
81138
"""Generic handler for post_save signal."""
82139
if sender == IpReport and created: # Track first IP report
83140
assign_first_action_badge(instance.user, "First IP Reported")
84-
giveBacon(instance.user)
141+
giveBacon(instance.user, instance=instance, action_type="created")
85142
create_activity(instance, "created")
86143

87144
elif sender == Post and created: # Track first blog post
88145
assign_first_action_badge(instance.author, "First Blog Posted")
89146
create_activity(instance, "created")
90-
giveBacon(instance.author, 5) # Reward for posting a blog
147+
giveBacon(instance.author, instance=instance, action_type="created")
91148

92149
elif sender == Issue and created: # Track first bug report
93150
assign_first_action_badge(instance.user, "First Bug Reported")
94151
create_activity(instance, "created")
95-
giveBacon(instance.user, 5) # Reward for reporting a bug
152+
giveBacon(instance.user, instance=instance, action_type="created")
96153

97154
elif sender == Hunt and created: # Track first bug bounty
98155
# Attempt to get the user from Domain managers or Organization
@@ -108,17 +165,17 @@ def handle_post_save(sender, instance, created, **kwargs):
108165
if user:
109166
assign_first_action_badge(user, "First Bug Bounty")
110167
create_activity(instance, "created")
111-
giveBacon(user, 5) # Reward for creating a bug bounty
168+
giveBacon(user, instance=instance, action_type="created")
112169

113170
elif sender == ForumPost and created: # Track first forum post
114171
assign_first_action_badge(instance.user, "First Forum Post")
115172
create_activity(instance, "created")
116-
giveBacon(instance.user, 2) # Reward for posting in forum
173+
giveBacon(instance.user, instance=instance, action_type="created")
117174

118175
elif sender == Bid and created: # Track first bid placed
119176
assign_first_action_badge(instance.user, "First Bid Placed")
120177
create_activity(instance, "placed")
121-
giveBacon(instance.user, 2) # Reward for placing a bid
178+
giveBacon(instance.user, instance=instance, action_type="placed")
122179

123180
elif sender is User and created: # Handle user sign-up
124181
Activity.objects.create(
@@ -158,9 +215,9 @@ def update_user_streak(sender, instance, created, **kwargs):
158215
def handle_organization_creation(sender, instance, created, **kwargs):
159216
"""Give bacon to user when they create an organization"""
160217
if created and instance.admin:
161-
# Give 10 bacon tokens for creating an organization
162-
giveBacon(instance.admin, 10)
163-
# Create an activity for the organization creation
218+
# Create an activity first so it's included in the AI analysis
164219
create_activity(instance, "created")
220+
# Give bacon tokens using AI analysis or fallback to default (10)
221+
giveBacon(instance.admin, instance=instance, action_type="created")
165222
# Give first organization badge
166223
assign_first_action_badge(instance.admin, "First Organization Created")

website/templates/gsoc.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,9 @@ <h3 class="text-xl font-semibold text-gray-900">Sudhir Palavalasa</h3>
678678
<h3 class="text-xl font-semibold text-gray-900">Ahmed ElSheikh</h3>
679679
</div>
680680
</div>
681-
<p class="text-gray-700">As a cybersecurity analyst and AI security engineer, I bring a strong background in threat detection, secure system design, and AI model protection. For GSoC 2025, I'm mentoring students on cutting-edge AI fingerprinting techniques and secure AI integrations in web applications. My goal is to empower contributors to build real-world, secure systems while learning to navigate the intersection of AI and cybersecurity.</p>
681+
<p class="text-gray-700">
682+
As a cybersecurity analyst and AI security engineer, I bring a strong background in threat detection, secure system design, and AI model protection. For GSoC 2025, I'm mentoring students on cutting-edge AI fingerprinting techniques and secure AI integrations in web applications. My goal is to empower contributors to build real-world, secure systems while learning to navigate the intersection of AI and cybersecurity.
683+
</p>
682684
</div>
683685
</div>
684686
</div>

website/utils.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,3 +1017,91 @@ def check_security_txt(domain_url):
10171017

10181018
# If we reach here, no security.txt was found
10191019
return False
1020+
1021+
1022+
def analyze_contribution(instance, action_type):
1023+
"""
1024+
Analyze a contribution using OpenAI to determine BACON token reward.
1025+
Returns a score between 1-50 based on complexity, impact, and quality.
1026+
"""
1027+
try:
1028+
# Extract relevant data from the instance
1029+
model_name = instance._meta.model_name
1030+
title = getattr(instance, "title", None) or getattr(instance, "description", None)
1031+
description = getattr(instance, "content", None) or getattr(instance, "body", None)
1032+
is_security = getattr(instance, "is_security", False)
1033+
1034+
# Construct the analysis prompt
1035+
prompt = f"""
1036+
Analyze this contribution and assign a BACON token reward score between 1 and 50.
1037+
1038+
Contribution Details:
1039+
- Type: {model_name}
1040+
- Action: {action_type}
1041+
- Title: {title}
1042+
- Description: {description}
1043+
- Security Related: {is_security}
1044+
1045+
Scoring Guidelines:
1046+
- Basic contributions (simple issues, comments): 1-5 BACON
1047+
- Standard contributions (well-documented issues, blog posts): 5-15 BACON
1048+
- Valuable contributions (detailed bug reports, tutorials): 15-25 BACON
1049+
- High-impact contributions (security vulnerabilities, major features): 25-50 BACON
1050+
1051+
Evaluation Criteria:
1052+
1. Technical complexity
1053+
2. Documentation quality
1054+
3. Security impact
1055+
4. Community benefit
1056+
5. Overall effort
1057+
1058+
Return only a number between 1 and 50.
1059+
"""
1060+
1061+
# Get response from OpenAI
1062+
response = client.chat.completions.create(
1063+
model="gpt-4",
1064+
messages=[
1065+
{"role": "system", "content": "You are evaluating contributions to determine BACON token rewards."},
1066+
{"role": "user", "content": prompt},
1067+
],
1068+
temperature=0.3,
1069+
max_tokens=10,
1070+
)
1071+
1072+
# Extract and validate the score
1073+
try:
1074+
score = int(float(response.choices[0].message.content.strip()))
1075+
# Ensure score is within bounds
1076+
score = max(1, min(50, score))
1077+
return score
1078+
except (ValueError, AttributeError):
1079+
# Default scores if parsing fails
1080+
return get_default_bacon_score(model_name, is_security)
1081+
1082+
except Exception as e:
1083+
logging.error(f"Error analyzing contribution for BACON score: {str(e)}")
1084+
return get_default_bacon_score(model_name, is_security)
1085+
1086+
1087+
def get_default_bacon_score(model_name, is_security=False):
1088+
"""
1089+
Get default BACON score based on contribution type.
1090+
"""
1091+
base_scores = {
1092+
"issue": 5,
1093+
"post": 10,
1094+
"hunt": 15,
1095+
"ipreport": 3,
1096+
"organization": 10,
1097+
"forumpost": 2,
1098+
}
1099+
1100+
# Get base score or default to 5
1101+
score = base_scores.get(model_name.lower(), 5)
1102+
1103+
# Add bonus for security-related content
1104+
if is_security:
1105+
score += 3
1106+
1107+
return score

0 commit comments

Comments
 (0)