@@ -134,7 +134,8 @@ def get_activity(
134134 -------
135135 query_data : pandas DataFrame
136136 A munged collection of data returned from your query. This
137- will be a combination of issues and PRs.
137+ will be a combination of issues and PRs. The DataFrame has a
138+ `bot_users` attribute containing the set of detected bot usernames.
138139 """
139140
140141 org , repo = _parse_target (target )
@@ -206,13 +207,16 @@ def get_activity(
206207 # Query for both opened and closed issues/PRs in this window
207208 print (f"Running search query:\n { search_query } \n \n " , file = sys .stderr )
208209 query_data = []
210+ all_bot_users = set ()
209211 for activity_type in ["created" , "closed" ]:
210212 ii_search_query = (
211213 search_query + f" { activity_type } :{ since_dt_str } ..{ until_dt_str } "
212214 )
213215 qu = GitHubGraphQlQuery (ii_search_query , auth = auth )
214216 qu .request ()
215217 query_data .append (qu .data )
218+ # Collect bot users from each query
219+ all_bot_users .update (qu .data .attrs .get ("bot_users" , set ()))
216220
217221 query_data = (
218222 pd .concat (query_data ).drop_duplicates (subset = ["id" ]).reset_index (drop = True )
@@ -223,9 +227,12 @@ def get_activity(
223227 query_data .until_dt_str = until_dt_str
224228 query_data .since_is_git_ref = since_is_git_ref
225229 query_data .until_is_git_ref = until_is_git_ref
230+ # Restore bot_users in attrs (lost during concat)
231+ query_data .attrs ["bot_users" ] = all_bot_users
226232
227233 if cache :
228234 _cache_data (query_data , cache )
235+
229236 return query_data
230237
231238
@@ -462,15 +469,34 @@ def generate_activity_md(
462469 data ["contributors" ] = [[]] * len (data )
463470
464471 # Get bot users from GraphQL data (stored in DataFrame attrs)
465- bot_users = data .attrs . get ( "bot_users" , set ())
472+ bot_users = data .attrs [ "bot_users" ]
466473
467474 def ignored_user (username ):
468- if username in bot_users :
475+ if not username :
476+ return False
477+
478+ # First check against GraphQL-detected bot users
479+ # It is common for a bot to have `username` in GitHub and `username[bot]` in commits.
480+ # So this accounts for that.
481+ normalized_username = username .replace ("[bot]" , "" )
482+ if normalized_username in bot_users :
483+ return True
484+
485+ # Next use pattern-based fallback for bots not detected by GraphQL
486+ username_lower = username .lower ()
487+ bot_patterns = [
488+ "[bot]" , # e.g., github-actions[bot], codecov[bot]
489+ "-bot" , # e.g., renovate-bot, release-bot, dependabot
490+ ]
491+ if any (pattern in username_lower for pattern in bot_patterns ):
469492 return True
493+
494+ # Check against user-specified ignored contributors
470495 if ignored_contributors and any (
471496 fnmatch .fnmatch (username , user ) for user in ignored_contributors
472497 ):
473498 return True
499+
474500 return False
475501
476502 def filter_ignored (userlist ):
@@ -490,12 +516,19 @@ def filter_ignored(userlist):
490516 # - merger
491517 # - reviewers
492518
493- item_contributors .author = row .author
519+ # Only add author if they're not a bot
520+ if not ignored_user (row .author ):
521+ item_contributors .author = row .author
494522
495523 if row .kind == "pr" :
496524 for committer in filter_ignored (row .committers ):
497525 item_contributors .add (committer )
498- if row .mergedBy and row .mergedBy != row .author :
526+ # Only add merger if they're not a bot and not the author
527+ if (
528+ row .mergedBy
529+ and row .mergedBy != row .author
530+ and not ignored_user (row .mergedBy )
531+ ):
499532 item_contributors .add (row .mergedBy )
500533 for reviewer in filter_ignored (row .reviewers ):
501534 item_contributors .add (reviewer )
0 commit comments