github-monitor 2.0__py3-none-any.whl → 2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of github-monitor might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github_monitor
3
- Version: 2.0
3
+ Version: 2.1
4
4
  Summary: Tool implementing real-time tracking of Github users activities including profile and repositories changes
5
5
  Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
6
  License-Expression: GPL-3.0-or-later
@@ -26,7 +26,7 @@ Dynamic: license-file
26
26
 
27
27
  # github_monitor
28
28
 
29
- github_monitor is a tool for real-time monitoring of GitHub users' activities, including profile and repository changes.
29
+ OSINT tool for real-time monitoring of GitHub users' activities, including profile and repository changes.
30
30
 
31
31
  <a id="features"></a>
32
32
  ## Features
@@ -292,6 +292,12 @@ If you want to monitor changes to user's public repositories (e.g. new stargazer
292
292
  github_monitor github_username -j
293
293
  ```
294
294
 
295
+ By default, only user-owned repos are tracked. To include forks and collaborations, set `GET_ALL_REPOS` to `True` or use the `-a` flag:
296
+
297
+ ```sh
298
+ github_monitor github_username -j -a
299
+ ```
300
+
295
301
  If for any reason you do not want to monitor GitHub events for the user (e.g. new pushes, PRs, issues, forks, releases etc.), then use the `-k` flag:
296
302
 
297
303
  ```sh
@@ -319,6 +325,12 @@ github_monitor github_username -r
319
325
  <img src="https://raw.githubusercontent.com/misiektoja/github_monitor/refs/heads/main/assets/github_list_of_repos.png" alt="github_list_of_repos" width="90%"/>
320
326
  </p>
321
327
 
328
+ By default, only user-owned repos are listed. To include forks and collaborations, set `GET_ALL_REPOS` to `True` or use the `-a` flag:
329
+
330
+ ```sh
331
+ github_monitor github_username -r -a
332
+ ```
333
+
322
334
  If you want to display a list of repositories starred by the user then use the `-g` flag:
323
335
 
324
336
  ```sh
@@ -0,0 +1,7 @@
1
+ github_monitor.py,sha256=TwfAtvASFyS-SFWZryolvv81Q0yoPhz_FBNjw2xXkBA,124098
2
+ github_monitor-2.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
3
+ github_monitor-2.1.dist-info/METADATA,sha256=kTwOp_S9Gq9baD6Gzl9k8s-0BGwVbU9MWuD3Zi7lkdM,17039
4
+ github_monitor-2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ github_monitor-2.1.dist-info/entry_points.txt,sha256=hV03y00u1L16S5BwBSLQvFsZcL2WGRtjzlrmu9U9SN0,55
6
+ github_monitor-2.1.dist-info/top_level.txt,sha256=HDN2988ydvH9JZT32PushzqrcD05Q5qg960vgHGIaI8,15
7
+ github_monitor-2.1.dist-info/RECORD,,
github_monitor.py CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
4
- v2.0
4
+ v2.1
5
5
 
6
6
  OSINT tool implementing real-time tracking of GitHub users activities including profile and repositories changes:
7
7
  https://github.com/misiektoja/github_monitor/
@@ -16,7 +16,7 @@ tzlocal (optional)
16
16
  python-dotenv (optional)
17
17
  """
18
18
 
19
- VERSION = "2.0"
19
+ VERSION = "2.1"
20
20
 
21
21
  # ---------------------------
22
22
  # CONFIGURATION SECTION START
@@ -32,8 +32,7 @@ CONFIG_BLOCK = """
32
32
  # - Pass it at runtime with -t / --github-token
33
33
  # - Set it as an environment variable (e.g. export GITHUB_TOKEN=...)
34
34
  # - Add it to ".env" file (GITHUB_TOKEN=...) for persistent use
35
- # Fallback:
36
- # - Hard-code it in the code or config file
35
+ # - Fallback: hard-code it in the code or config file
37
36
  GITHUB_TOKEN = "your_github_classic_personal_access_token"
38
37
 
39
38
  # The URL of the GitHub API
@@ -127,6 +126,13 @@ EVENTS_TO_MONITOR = [
127
126
  # any events older than the most recent EVENTS_NUMBER will be missed
128
127
  EVENTS_NUMBER = 30 # 1 page
129
128
 
129
+ # If True, fetch all user repos (owned, forks, collaborations); otherwise, fetch only owned repos
130
+ GET_ALL_REPOS = False
131
+
132
+ # Alert about blocked (403 - TOS violation and 451 - DMCA block) repos in the console output (in monitoring mode)
133
+ # In listing mode (-r), blocked repos are always shown
134
+ BLOCKED_REPOS = False
135
+
130
136
  # How often to print a "liveness check" message to the output; in seconds
131
137
  # Set to 0 to disable
132
138
  LIVENESS_CHECK_INTERVAL = 43200 # 12 hours
@@ -199,6 +205,8 @@ GITHUB_CHECK_INTERVAL = 0
199
205
  LOCAL_TIMEZONE = ""
200
206
  EVENTS_TO_MONITOR = []
201
207
  EVENTS_NUMBER = 0
208
+ GET_ALL_REPOS = False
209
+ BLOCKED_REPOS = False
202
210
  LIVENESS_CHECK_INTERVAL = 0
203
211
  CHECK_INTERNET_URL = ""
204
212
  CHECK_INTERNET_TIMEOUT = 0
@@ -911,6 +919,7 @@ def github_print_followers_and_followings(user):
911
919
 
912
920
  # Processes items from all passed repositories and returns a list of dictionaries
913
921
  def github_process_repos(repos_list):
922
+ import logging
914
923
  list_of_repos = []
915
924
  stargazers_list = []
916
925
  subscribers_list = []
@@ -921,9 +930,24 @@ def github_process_repos(repos_list):
921
930
  try:
922
931
  repo_created_date = repo.created_at
923
932
  repo_updated_date = repo.updated_at
924
- stargazers_list = [star.login for star in repo.get_stargazers()]
925
- subscribers_list = [subscriber.login for subscriber in repo.get_subscribers()]
926
- forked_repos = [fork.full_name for fork in repo.get_forks()]
933
+
934
+ github_logger = logging.getLogger('github')
935
+ original_level = github_logger.level
936
+ github_logger.setLevel(logging.ERROR)
937
+
938
+ try:
939
+ stargazers_list = [star.login for star in repo.get_stargazers()]
940
+ subscribers_list = [subscriber.login for subscriber in repo.get_subscribers()]
941
+ forked_repos = [fork.full_name for fork in repo.get_forks()]
942
+ except GithubException as e:
943
+ if e.status in [403, 451]:
944
+ if BLOCKED_REPOS:
945
+ print(f"* Repo '{repo.name}' is blocked, skipping for now: {e}")
946
+ print_cur_ts("Timestamp:\t\t\t")
947
+ continue
948
+ raise
949
+ finally:
950
+ github_logger.setLevel(original_level)
927
951
 
928
952
  issues = list(repo.get_issues(state='open'))
929
953
  pulls = list(repo.get_pulls(state='open'))
@@ -936,8 +960,20 @@ def github_process_repos(repos_list):
936
960
  pr_list = [f"#{pr.number} {pr.title} ({pr.user.login}) [ {pr.html_url} ]" for pr in pulls]
937
961
 
938
962
  list_of_repos.append({"name": repo.name, "descr": repo.description, "is_fork": repo.fork, "forks": repo.forks_count, "stars": repo.stargazers_count, "subscribers": repo.subscribers_count, "url": repo.html_url, "language": repo.language, "date": repo_created_date, "update_date": repo_updated_date, "stargazers_list": stargazers_list, "forked_repos": forked_repos, "subscribers_list": subscribers_list, "issues": issue_count, "pulls": pr_count, "issues_list": issues_list, "pulls_list": pr_list})
963
+
964
+ except GithubException as e:
965
+ # Skip TOS-blocked (403) and legally blocked (451) repositories
966
+ if e.status in [403, 451]:
967
+ if BLOCKED_REPOS:
968
+ print(f"* Repo '{repo.name}' is blocked, skipping for now: {e}")
969
+ print_cur_ts("Timestamp:\t\t\t")
970
+ continue
971
+ else:
972
+ print(f"* Cannot process repo '{repo.name}', skipping for now: {e}")
973
+ print_cur_ts("Timestamp:\t\t\t")
974
+ continue
939
975
  except Exception as e:
940
- print(f"* Error while processing info for repo '{repo.name}', skipping for now: {e}")
976
+ print(f"* Cannot process repo '{repo.name}', skipping for now: {e}")
941
977
  print_cur_ts("Timestamp:\t\t\t")
942
978
  continue
943
979
 
@@ -946,6 +982,7 @@ def github_process_repos(repos_list):
946
982
 
947
983
  # Prints a list of public repositories for a GitHub user (-r)
948
984
  def github_print_repos(user):
985
+ import logging
949
986
  user_name_str = user
950
987
  user_url = "-"
951
988
  repos_count = 0
@@ -962,8 +999,12 @@ def github_print_repos(user):
962
999
  user_name = g_user.name
963
1000
  user_url = g_user.html_url
964
1001
 
965
- repos_count = g_user.public_repos
966
- repos_list = g_user.get_repos()
1002
+ if GET_ALL_REPOS:
1003
+ repos_list = g_user.get_repos()
1004
+ repos_count = g_user.public_repos
1005
+ else:
1006
+ repos_list = [repo for repo in g_user.get_repos(type='owner') if not repo.fork and repo.owner.login == user_login]
1007
+ repos_count = len(repos_list)
967
1008
 
968
1009
  user_name_str = user_login
969
1010
  if user_name:
@@ -974,6 +1015,7 @@ def github_print_repos(user):
974
1015
  print(f"\nUsername:\t\t{user_name_str}")
975
1016
  print(f"User URL:\t\t{user_url}/")
976
1017
  print(f"GitHub API URL:\t\t{GITHUB_API_URL}")
1018
+ print(f"Owned repos only:\t{not GET_ALL_REPOS}")
977
1019
  print(f"Local timezone:\t\t{LOCAL_TIMEZONE}")
978
1020
 
979
1021
  print(f"\nRepositories:\t\t{repos_count}\n")
@@ -984,6 +1026,10 @@ def github_print_repos(user):
984
1026
  for repo in repos_list:
985
1027
  print(f"🔸 {repo.name} {'(fork)' if repo.fork else ''} \n")
986
1028
 
1029
+ github_logger = logging.getLogger('github')
1030
+ original_level = github_logger.level
1031
+ github_logger.setLevel(logging.ERROR)
1032
+
987
1033
  try:
988
1034
  pr_count = repo.get_pulls(state='open').totalCount
989
1035
  issue_count = repo.open_issues_count - pr_count
@@ -991,26 +1037,36 @@ def github_print_repos(user):
991
1037
  pr_count = "?"
992
1038
  issue_count = "?"
993
1039
 
994
- print(f" - 🌐 URL:\t\t{repo.html_url}")
995
- print(f" - 💻 Language:\t\t{repo.language}")
996
-
997
- print(f"\n - ⭐ Stars:\t\t{repo.stargazers_count}")
998
- print(f" - 🍴 Forks:\t\t{repo.forks_count}")
999
- print(f" - 👓 Watchers:\t\t{repo.subscribers_count}")
1000
-
1001
- # print(f" - 🐞 Issues+PRs:\t{repo.open_issues_count}")
1002
- print(f" - 🐞 Issues:\t\t{issue_count}")
1003
- print(f" - 📬 PRs:\t\t{pr_count}")
1004
-
1005
- print(f"\n - 📝 License:\t\t{repo.license.name if repo.license else 'None'}")
1006
- print(f" - 🌿 Branch (default):\t{repo.default_branch}")
1007
-
1008
- print(f"\n - 📅 Created:\t\t{get_date_from_ts(repo.created_at)} ({calculate_timespan(int(time.time()), repo.created_at, granularity=2)} ago)")
1009
- print(f" - 🔄 Updated:\t\t{get_date_from_ts(repo.updated_at)} ({calculate_timespan(int(time.time()), repo.updated_at, granularity=2)} ago)")
1010
- print(f" - 🔃 Last push:\t{get_date_from_ts(repo.pushed_at)} ({calculate_timespan(int(time.time()), repo.pushed_at, granularity=2)} ago)")
1040
+ try:
1041
+ print(f" - 🌐 URL:\t\t{repo.html_url}")
1042
+ print(f" - 💻 Language:\t\t{repo.language}")
1043
+
1044
+ print(f"\n - Stars:\t\t{repo.stargazers_count}")
1045
+ print(f" - 🍴 Forks:\t\t{repo.forks_count}")
1046
+ print(f" - 👓 Watchers:\t\t{repo.subscribers_count}")
1047
+
1048
+ # print(f" - 🐞 Issues+PRs:\t{repo.open_issues_count}")
1049
+ print(f" - 🐞 Issues:\t\t{issue_count}")
1050
+ print(f" - 📬 PRs:\t\t{pr_count}")
1051
+
1052
+ print(f"\n - 📝 License:\t\t{repo.license.name if repo.license else 'None'}")
1053
+ print(f" - 🌿 Branch (default):\t{repo.default_branch}")
1054
+
1055
+ print(f"\n - 📅 Created:\t\t{get_date_from_ts(repo.created_at)} ({calculate_timespan(int(time.time()), repo.created_at, granularity=2)} ago)")
1056
+ print(f" - 🔄 Updated:\t\t{get_date_from_ts(repo.updated_at)} ({calculate_timespan(int(time.time()), repo.updated_at, granularity=2)} ago)")
1057
+ print(f" - 🔃 Last push:\t{get_date_from_ts(repo.pushed_at)} ({calculate_timespan(int(time.time()), repo.pushed_at, granularity=2)} ago)")
1058
+
1059
+ if repo.description:
1060
+ print(f"\n - 📝 Desc:\t\t{repo.description}")
1061
+ except GithubException as e:
1062
+ # Inform about TOS-blocked (403) and legally blocked (451) repositories
1063
+ if e.status in [403, 451]:
1064
+ print(f"\n* Repo '{repo.name}' is blocked: {e}")
1065
+ print("─" * HORIZONTAL_LINE2)
1066
+ continue
1067
+ finally:
1068
+ github_logger.setLevel(original_level)
1011
1069
 
1012
- if repo.description:
1013
- print(f"\n - 📝 Desc:\t\t{repo.description}")
1014
1070
  print("─" * HORIZONTAL_LINE2)
1015
1071
  except Exception as e:
1016
1072
  raise RuntimeError(f"Cannot fetch user's repositories list: {e}")
@@ -1971,11 +2027,16 @@ def github_monitor_user(user, csv_file_name):
1971
2027
 
1972
2028
  followers_count = g_user.followers
1973
2029
  followings_count = g_user.following
1974
- repos_count = g_user.public_repos
1975
2030
 
1976
2031
  followers_list = g_user.get_followers()
1977
2032
  followings_list = g_user.get_following()
1978
- repos_list = g_user.get_repos()
2033
+
2034
+ if GET_ALL_REPOS:
2035
+ repos_list = g_user.get_repos()
2036
+ repos_count = g_user.public_repos
2037
+ else:
2038
+ repos_list = [repo for repo in g_user.get_repos(type='owner') if not repo.fork and repo.owner.login == user_login]
2039
+ repos_count = len(repos_list)
1979
2040
 
1980
2041
  starred_list = g_user.get_starred()
1981
2042
  starred_count = starred_list.totalCount
@@ -2164,8 +2225,13 @@ def github_monitor_user(user, csv_file_name):
2164
2225
  followers_old, followers_old_count = handle_profile_change("Followers", followers_old_count, followers_count, followers_old, followers_raw, user, csv_file_name, field="login")
2165
2226
 
2166
2227
  # Changed public repositories
2167
- repos_raw = list(gh_call(g_user.get_repos)())
2168
- repos_count = gh_call(lambda: g_user.public_repos)()
2228
+ if GET_ALL_REPOS:
2229
+ repos_raw = list(gh_call(g_user.get_repos)())
2230
+ repos_count = gh_call(lambda: g_user.public_repos)()
2231
+ else:
2232
+ repos_raw = list(gh_call(lambda: [repo for repo in g_user.get_repos(type='owner') if not repo.fork and repo.owner.login == user_login])())
2233
+ repos_count = len(repos_raw)
2234
+
2169
2235
  if repos_raw is not None and repos_count is not None:
2170
2236
  repos_old, repos_old_count = handle_profile_change("Repos", repos_old_count, repos_count, repos_old, repos_raw, user, csv_file_name, field="name")
2171
2237
 
@@ -2404,7 +2470,12 @@ def github_monitor_user(user, csv_file_name):
2404
2470
 
2405
2471
  # Changed repos details
2406
2472
  if TRACK_REPOS_CHANGES:
2407
- repos_list = gh_call(g_user.get_repos)()
2473
+
2474
+ if GET_ALL_REPOS:
2475
+ repos_list = gh_call(g_user.get_repos)()
2476
+ else:
2477
+ repos_list = gh_call(lambda: [repo for repo in g_user.get_repos(type='owner') if not repo.fork and repo.owner.login == user_login])()
2478
+
2408
2479
  if repos_list is not None:
2409
2480
  try:
2410
2481
  list_of_repos = github_process_repos(repos_list)
@@ -2581,7 +2652,7 @@ def github_monitor_user(user, csv_file_name):
2581
2652
 
2582
2653
 
2583
2654
  def main():
2584
- global CLI_CONFIG_PATH, DOTENV_FILE, LOCAL_TIMEZONE, LIVENESS_CHECK_COUNTER, GITHUB_TOKEN, GITHUB_API_URL, CSV_FILE, DISABLE_LOGGING, GITHUB_LOGFILE, PROFILE_NOTIFICATION, EVENT_NOTIFICATION, REPO_NOTIFICATION, REPO_UPDATE_DATE_NOTIFICATION, ERROR_NOTIFICATION, GITHUB_CHECK_INTERVAL, SMTP_PASSWORD, stdout_bck, DO_NOT_MONITOR_GITHUB_EVENTS, TRACK_REPOS_CHANGES
2655
+ global CLI_CONFIG_PATH, DOTENV_FILE, LOCAL_TIMEZONE, LIVENESS_CHECK_COUNTER, GITHUB_TOKEN, GITHUB_API_URL, CSV_FILE, DISABLE_LOGGING, GITHUB_LOGFILE, PROFILE_NOTIFICATION, EVENT_NOTIFICATION, REPO_NOTIFICATION, REPO_UPDATE_DATE_NOTIFICATION, ERROR_NOTIFICATION, GITHUB_CHECK_INTERVAL, SMTP_PASSWORD, stdout_bck, DO_NOT_MONITOR_GITHUB_EVENTS, TRACK_REPOS_CHANGES, GET_ALL_REPOS
2585
2656
 
2586
2657
  if "--generate-config" in sys.argv:
2587
2658
  print(CONFIG_BLOCK.strip("\n"))
@@ -2766,6 +2837,13 @@ def main():
2766
2837
  default=None,
2767
2838
  help="Disable event monitoring"
2768
2839
  )
2840
+ opts.add_argument(
2841
+ "-a", "--get-all-repos",
2842
+ dest="get_all_repos",
2843
+ action="store_true",
2844
+ default=None,
2845
+ help="Fetch all user repos (owned, forks, collaborations)"
2846
+ )
2769
2847
  opts.add_argument(
2770
2848
  "-b", "--csv-file",
2771
2849
  dest="csv_file",
@@ -2883,6 +2961,9 @@ def main():
2883
2961
  print("* Error: GITHUB_API_URL (-x / --github_url) value is empty")
2884
2962
  sys.exit(1)
2885
2963
 
2964
+ if args.get_all_repos is True:
2965
+ GET_ALL_REPOS = True
2966
+
2886
2967
  if args.list_followers_and_followings:
2887
2968
  try:
2888
2969
  github_print_followers_and_followings(args.username)
@@ -2994,6 +3075,7 @@ def main():
2994
3075
  print(f"* GitHub API URL:\t\t{GITHUB_API_URL}")
2995
3076
  print(f"* Track repos changes:\t\t{TRACK_REPOS_CHANGES}")
2996
3077
  print(f"* Monitor GitHub events:\t{not DO_NOT_MONITOR_GITHUB_EVENTS}")
3078
+ print(f"* Get owned repos only:\t\t{not GET_ALL_REPOS}")
2997
3079
  print(f"* Liveness check:\t\t{bool(LIVENESS_CHECK_INTERVAL)}" + (f" ({display_time(LIVENESS_CHECK_INTERVAL)})" if LIVENESS_CHECK_INTERVAL else ""))
2998
3080
  print(f"* CSV logging enabled:\t\t{bool(CSV_FILE)}" + (f" ({CSV_FILE})" if CSV_FILE else ""))
2999
3081
  print(f"* Output logging enabled:\t{not DISABLE_LOGGING}" + (f" ({FINAL_LOG_PATH})" if not DISABLE_LOGGING else ""))
@@ -1,7 +0,0 @@
1
- github_monitor.py,sha256=1ArEATU7rgs_SQp5bP1y1txl278aXAKQtFKk-5pJi-I,120554
2
- github_monitor-2.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
3
- github_monitor-2.0.dist-info/METADATA,sha256=yjYLu0so8L8Y1AxTmnoHalDhqjwCVPqn7MXPuYn427M,16684
4
- github_monitor-2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
- github_monitor-2.0.dist-info/entry_points.txt,sha256=hV03y00u1L16S5BwBSLQvFsZcL2WGRtjzlrmu9U9SN0,55
6
- github_monitor-2.0.dist-info/top_level.txt,sha256=HDN2988ydvH9JZT32PushzqrcD05Q5qg960vgHGIaI8,15
7
- github_monitor-2.0.dist-info/RECORD,,