github-monitor 1.9.1__py3-none-any.whl → 2.0__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: 1.9.1
3
+ Version: 2.0
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
@@ -38,7 +38,9 @@ github_monitor is a tool for real-time monitoring of GitHub users' activities, i
38
38
  - added/removed starred repositories
39
39
  - added/removed public repositories
40
40
  - changes in user name, email, location, company, bio and blog URL
41
- - detection of account changes
41
+ - changes in profile visibility (public to private and vice versa)
42
+ - detection when a user blocks or unblocks you
43
+ - detection of account metadata changes (such as account update date)
42
44
  - Email notifications for different events (new GitHub events, changed followings, followers, repositories, user name, email, location, company, bio, blog URL etc.)
43
45
  - Saving all user activities with timestamps to the CSV file
44
46
  - Clickable GitHub URLs printed in the console & included in email notifications (repos, PRs, commits, issues, releases etc.)
@@ -167,18 +169,16 @@ Provide the `GITHUB_TOKEN` secret using one of the following methods:
167
169
  - Pass it at runtime with `-t` / `--github-token`
168
170
  - Set it as an [environment variable](#storing-secrets) (e.g. `export GITHUB_TOKEN=...`)
169
171
  - Add it to [.env file](#storing-secrets) (`GITHUB_TOKEN=...`) for persistent use
172
+ - Fallback: hard-code it in the code or config file
170
173
 
171
- Fallback:
172
- - Hard-code it in the code or config file
173
-
174
- If you store the `GITHUB_TOKEN` in a dotenv file you can update its value and send a `SIGHUP` signal to the process to reload the file with the new token without restarting the tool. More info in [Storing Secrets](#storing-secrets) and [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix).
174
+ If you store the `GITHUB_TOKEN` in a dotenv file you can update its value and send a `SIGHUP` signal to reload the file with the new token without restarting the tool. More info in [Storing Secrets](#storing-secrets) and [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix).
175
175
 
176
176
  <a id="github-api-url"></a>
177
177
  ### GitHub API URL
178
178
 
179
179
  By default the tool uses Public Web GitHub API URL: [https://api.github.com](https://api.github.com)
180
180
 
181
- If you want to use GitHub Enterprise API URL then change `GITHUB_API_URL` (or use `-x` flag) to: https://{your_hostname}/api/v3
181
+ If you want to use GitHub Enterprise API URL then change `GITHUB_API_URL` (or use `-x` flag) to: `https://{your_hostname}/api/v3`
182
182
 
183
183
 
184
184
  <a id="events-to-monitor"></a>
@@ -0,0 +1,7 @@
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,,
github_monitor.py CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
4
- v1.9
4
+ v2.0
5
5
 
6
- OSINT tool implementing real-time tracking of Github users activities including profile and repositories changes:
6
+ OSINT tool implementing real-time tracking of GitHub users activities including profile and repositories changes:
7
7
  https://github.com/misiektoja/github_monitor/
8
8
 
9
9
  Python pip3 requirements:
@@ -16,14 +16,14 @@ tzlocal (optional)
16
16
  python-dotenv (optional)
17
17
  """
18
18
 
19
- VERSION = "1.9.1"
19
+ VERSION = "2.0"
20
20
 
21
21
  # ---------------------------
22
22
  # CONFIGURATION SECTION START
23
23
  # ---------------------------
24
24
 
25
25
  CONFIG_BLOCK = """
26
- # Get your Github personal access token (classic) by visiting:
26
+ # Get your GitHub personal access token (classic) by visiting:
27
27
  # https://github.com/settings/apps
28
28
  #
29
29
  # Then go to: Personal access tokens -> Tokens (classic) -> Generate new token (classic)
@@ -36,14 +36,21 @@ CONFIG_BLOCK = """
36
36
  # - Hard-code it in the code or config file
37
37
  GITHUB_TOKEN = "your_github_classic_personal_access_token"
38
38
 
39
- # The URL of the Github API
39
+ # The URL of the GitHub API
40
40
  #
41
- # For Public Web Github use the default: https://api.github.com
42
- # For Github Enterprise change to: https://{your_hostname}/api/v3
41
+ # For Public Web GitHub use the default: https://api.github.com
42
+ # For GitHub Enterprise change to: https://{your_hostname}/api/v3
43
43
  #
44
44
  # Can also be set using the -x flag
45
45
  GITHUB_API_URL = "https://api.github.com"
46
46
 
47
+ # The base URL of the GitHub web interface
48
+ # Required to check if the profile is public or private
49
+ #
50
+ # For public GitHub use the default: https://github.com
51
+ # For GitHub Enterprise change to: https://{your_hostname}
52
+ GITHUB_HTML_URL = "https://github.com"
53
+
47
54
  # SMTP settings for sending email notifications
48
55
  # If left as-is, no notifications will be sent
49
56
  #
@@ -85,7 +92,7 @@ ERROR_NOTIFICATION = True
85
92
  # Can also be set using the -c flag
86
93
  GITHUB_CHECK_INTERVAL = 1800 # 30 mins
87
94
 
88
- # Set your local time zone so that Github API timestamps are converted accordingly (e.g. 'Europe/Warsaw')
95
+ # Set your local time zone so that GitHub API timestamps are converted accordingly (e.g. 'Europe/Warsaw')
89
96
  # Use this command to list all time zones supported by pytz:
90
97
  # python3 -c "import pytz; print('\\n'.join(pytz.all_timezones))"
91
98
  # If set to 'Auto', the tool will try to detect your local time zone automatically (requires tzlocal)
@@ -175,6 +182,7 @@ GITHUB_CHECK_SIGNAL_VALUE = 60 # 1 minute
175
182
  # Do not change values below - modify them in the configuration section or config file instead
176
183
  GITHUB_TOKEN = ""
177
184
  GITHUB_API_URL = ""
185
+ GITHUB_HTML_URL = ""
178
186
  SMTP_HOST = ""
179
187
  SMTP_PORT = 0
180
188
  SMTP_USER = ""
@@ -721,7 +729,7 @@ def toggle_profile_changes_notifications_signal_handler(sig, frame):
721
729
  PROFILE_NOTIFICATION = not PROFILE_NOTIFICATION
722
730
  sig_name = signal.Signals(sig).name
723
731
  print(f"* Signal {sig_name} received")
724
- print(f"* Email notifications: [profile changes = {PROFILE_NOTIFICATION}]")
732
+ print(f"* Email notifications:\t\t[profile changes = {PROFILE_NOTIFICATION}]")
725
733
  print_cur_ts("Timestamp:\t\t\t")
726
734
 
727
735
 
@@ -731,7 +739,7 @@ def toggle_new_events_notifications_signal_handler(sig, frame):
731
739
  EVENT_NOTIFICATION = not EVENT_NOTIFICATION
732
740
  sig_name = signal.Signals(sig).name
733
741
  print(f"* Signal {sig_name} received")
734
- print(f"* Email notifications: [new events = {EVENT_NOTIFICATION}]")
742
+ print(f"* Email notifications:\t\t[new events = {EVENT_NOTIFICATION}]")
735
743
  print_cur_ts("Timestamp:\t\t\t")
736
744
 
737
745
 
@@ -741,7 +749,7 @@ def toggle_repo_changes_notifications_signal_handler(sig, frame):
741
749
  REPO_NOTIFICATION = not REPO_NOTIFICATION
742
750
  sig_name = signal.Signals(sig).name
743
751
  print(f"* Signal {sig_name} received")
744
- print(f"* Email notifications: [repos changes = {REPO_NOTIFICATION}]")
752
+ print(f"* Email notifications:\t\t[repos changes = {REPO_NOTIFICATION}]")
745
753
  print_cur_ts("Timestamp:\t\t\t")
746
754
 
747
755
 
@@ -751,7 +759,7 @@ def toggle_repo_update_date_changes_notifications_signal_handler(sig, frame):
751
759
  REPO_UPDATE_DATE_NOTIFICATION = not REPO_UPDATE_DATE_NOTIFICATION
752
760
  sig_name = signal.Signals(sig).name
753
761
  print(f"* Signal {sig_name} received")
754
- print(f"* Email notifications: [repos update date = {REPO_UPDATE_DATE_NOTIFICATION}]")
762
+ print(f"* Email notifications:\t\t[repos update date = {REPO_UPDATE_DATE_NOTIFICATION}]")
755
763
  print_cur_ts("Timestamp:\t\t\t")
756
764
 
757
765
 
@@ -761,7 +769,7 @@ def increase_check_signal_handler(sig, frame):
761
769
  GITHUB_CHECK_INTERVAL = GITHUB_CHECK_INTERVAL + GITHUB_CHECK_SIGNAL_VALUE
762
770
  sig_name = signal.Signals(sig).name
763
771
  print(f"* Signal {sig_name} received")
764
- print(f"* Github timers: [check interval: {display_time(GITHUB_CHECK_INTERVAL)}]")
772
+ print(f"* GitHub polling interval:\t[ {display_time(GITHUB_CHECK_INTERVAL)} ]")
765
773
  print_cur_ts("Timestamp:\t\t\t")
766
774
 
767
775
 
@@ -772,7 +780,7 @@ def decrease_check_signal_handler(sig, frame):
772
780
  GITHUB_CHECK_INTERVAL = GITHUB_CHECK_INTERVAL - GITHUB_CHECK_SIGNAL_VALUE
773
781
  sig_name = signal.Signals(sig).name
774
782
  print(f"* Signal {sig_name} received")
775
- print(f"* Github timers: [check interval: {display_time(GITHUB_CHECK_INTERVAL)}]")
783
+ print(f"* GitHub polling interval:\t[ {display_time(GITHUB_CHECK_INTERVAL)} ]")
776
784
  print_cur_ts("Timestamp:\t\t\t")
777
785
 
778
786
 
@@ -865,7 +873,7 @@ def github_print_followers_and_followings(user):
865
873
 
866
874
  print(f"\nUsername:\t\t{user_name_str}")
867
875
  print(f"User URL:\t\t{user_url}/")
868
- print(f"Github API URL:\t\t{GITHUB_API_URL}")
876
+ print(f"GitHub API URL:\t\t{GITHUB_API_URL}")
869
877
  print(f"Local timezone:\t\t{LOCAL_TIMEZONE}")
870
878
 
871
879
  print(f"\nFollowers:\t\t{followers_count}")
@@ -965,7 +973,7 @@ def github_print_repos(user):
965
973
 
966
974
  print(f"\nUsername:\t\t{user_name_str}")
967
975
  print(f"User URL:\t\t{user_url}/")
968
- print(f"Github API URL:\t\t{GITHUB_API_URL}")
976
+ print(f"GitHub API URL:\t\t{GITHUB_API_URL}")
969
977
  print(f"Local timezone:\t\t{LOCAL_TIMEZONE}")
970
978
 
971
979
  print(f"\nRepositories:\t\t{repos_count}\n")
@@ -1039,7 +1047,7 @@ def github_print_starred_repos(user):
1039
1047
 
1040
1048
  print(f"\nUsername:\t\t{user_name_str}")
1041
1049
  print(f"User URL:\t\t{user_url}/")
1042
- print(f"Github API URL:\t\t{GITHUB_API_URL}")
1050
+ print(f"GitHub API URL:\t\t{GITHUB_API_URL}")
1043
1051
  print(f"Local timezone:\t\t{LOCAL_TIMEZONE}")
1044
1052
 
1045
1053
  print(f"\nRepos starred by user:\t{starred_count}")
@@ -1557,7 +1565,7 @@ def github_list_events(user, number, csv_file_name):
1557
1565
 
1558
1566
  print(f"Username:\t\t\t{user_name_str}")
1559
1567
  print(f"User URL:\t\t\t{user_url}/")
1560
- print(f"Github API URL:\t\t\t{GITHUB_API_URL}")
1568
+ print(f"GitHub API URL:\t\t\t{GITHUB_API_URL}")
1561
1569
  if csv_file_name:
1562
1570
  print(f"CSV export enabled:\t\t{bool(csv_file_name)}" + (f" ({csv_file_name})" if csv_file_name else ""))
1563
1571
  print(f"Local timezone:\t\t\t{LOCAL_TIMEZONE}")
@@ -1664,12 +1672,12 @@ def handle_profile_change(label, count_old, count_new, list_old, raw_list, user,
1664
1672
  print()
1665
1673
 
1666
1674
  if diff == 0:
1667
- m_subject = f"Github user {user} {label.lower()} list changed"
1675
+ m_subject = f"GitHub user {user} {label.lower()} list changed"
1668
1676
  m_body = (f"{label} list changed {label_context} user {user}\n"
1669
1677
  f"{removed_mbody}{removed_list_str}{added_mbody}{added_list_str}\n"
1670
1678
  f"Check interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}")
1671
1679
  else:
1672
- m_subject = f"Github user {user} {label.lower()} number has changed! ({diff_str}, {old_count} -> {new_count})"
1680
+ m_subject = f"GitHub user {user} {label.lower()} number has changed! ({diff_str}, {old_count} -> {new_count})"
1673
1681
  m_body = (f"{label} number changed {label_context} user {user} from {old_count} to {new_count} ({diff_str})\n"
1674
1682
  f"{removed_mbody}{removed_list_str}{added_mbody}{added_list_str}\n"
1675
1683
  f"Check interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}")
@@ -1752,12 +1760,12 @@ def check_repo_list_changes(count_old, count_new, list_old, list_new, label, rep
1752
1760
  print()
1753
1761
 
1754
1762
  if diff == 0:
1755
- m_subject = f"Github user {user} {label.lower()} list changed for repo '{repo_name}'!"
1763
+ m_subject = f"GitHub user {user} {label.lower()} list changed for repo '{repo_name}'!"
1756
1764
  m_body = (f"* Repo '{repo_name}': {label.lower()} list changed\n"
1757
1765
  f"* Repo URL: {repo_url}\n{removed_mbody}{removed_list_str}{added_mbody}{added_list_str}\n"
1758
1766
  f"Check interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}")
1759
1767
  else:
1760
- m_subject = f"Github user {user} number of {label.lower()} for repo '{repo_name}' has changed! ({diff_str}, {old_count} -> {new_count})"
1768
+ m_subject = f"GitHub user {user} number of {label.lower()} for repo '{repo_name}' has changed! ({diff_str}, {old_count} -> {new_count})"
1761
1769
  m_body = (f"* Repo '{repo_name}': number of {label.lower()} changed from {old_count} to {new_count} ({diff_str})\n"
1762
1770
  f"* Repo URL: {repo_url}\n{removed_mbody}{removed_list_str}{added_mbody}{added_list_str}\n"
1763
1771
  f"Check interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}")
@@ -1807,7 +1815,119 @@ def resolve_executable(path):
1807
1815
  raise FileNotFoundError(f"Could not find executable '{path}'")
1808
1816
 
1809
1817
 
1810
- # Main function that monitors activity of the specified GitHub user
1818
+ # Checks if the authenticated user (token's owner) is blocked by user
1819
+ def is_blocked_by(user):
1820
+ try:
1821
+
1822
+ headers = {
1823
+ "Authorization": f"Bearer {GITHUB_TOKEN}",
1824
+ "Accept": "application/vnd.github+json",
1825
+ }
1826
+
1827
+ response = req.get(f"{GITHUB_API_URL}/user", headers=headers, timeout=15)
1828
+ if response.status_code != 200:
1829
+ return False
1830
+ me_login = response.json().get("login", "").lower()
1831
+ if user.lower() == me_login:
1832
+ return False
1833
+
1834
+ graphql_endpoint = GITHUB_API_URL.rstrip("/") + "/graphql"
1835
+ query = """
1836
+ query($login: String!) {
1837
+ user(login: $login) {
1838
+ viewerCanFollow
1839
+ }
1840
+ }
1841
+ """
1842
+ payload = {"query": query, "variables": {"login": user}}
1843
+ response_graphql = req.post(graphql_endpoint, json=payload, headers=headers, timeout=15)
1844
+
1845
+ if response_graphql.status_code == 404:
1846
+ return False
1847
+
1848
+ if not response_graphql.ok:
1849
+ return False
1850
+
1851
+ data = response_graphql.json()
1852
+ can_follow = (data.get("data", {}).get("user", {}).get("viewerCanFollow", True))
1853
+ return not bool(can_follow)
1854
+
1855
+ except Exception:
1856
+ return False
1857
+
1858
+
1859
+ # Return the total number of repositories the user has starred (faster than via PyGithub)
1860
+ def get_starred_count(user):
1861
+ try:
1862
+
1863
+ headers = {
1864
+ "Authorization": f"Bearer {GITHUB_TOKEN}",
1865
+ "Accept": "application/vnd.github+json",
1866
+ }
1867
+
1868
+ graphql_endpoint = f"{GITHUB_API_URL.rstrip('/')}/graphql"
1869
+ query = """
1870
+ query($login:String!){
1871
+ user(login:$login){
1872
+ starredRepositories{
1873
+ totalCount
1874
+ }
1875
+ }
1876
+ }
1877
+ """
1878
+ payload = {"query": query, "variables": {"login": user}}
1879
+ response = req.post(graphql_endpoint, json=payload, headers=headers, timeout=15)
1880
+
1881
+ if not response.ok:
1882
+ return 0
1883
+
1884
+ data = response.json()
1885
+
1886
+ return (data.get("data", {}).get("user", {}).get("starredRepositories", {}).get("totalCount", 0))
1887
+
1888
+ except Exception:
1889
+ return 0
1890
+
1891
+
1892
+ # Returns True if the user's GitHub page shows "activity is private"
1893
+ def has_private_banner(user):
1894
+ try:
1895
+ url = f"{GITHUB_HTML_URL.rstrip('/')}/{user}"
1896
+ r = req.get(url, timeout=15)
1897
+ return r.ok and "activity is private" in r.text.lower()
1898
+ except Exception:
1899
+ return False
1900
+
1901
+
1902
+ # Returns True if the user's GitHub profile is public
1903
+ def is_profile_public(g: Github, user, new_account_days=30):
1904
+
1905
+ if has_private_banner(user):
1906
+ return False
1907
+
1908
+ try:
1909
+ u = g.get_user(user)
1910
+
1911
+ if any([
1912
+ u.followers > 0,
1913
+ u.following > 0,
1914
+ get_starred_count(user) > 0,
1915
+ ]):
1916
+ return True
1917
+
1918
+ try:
1919
+ events_iter = iter(u.get_events())
1920
+ next(events_iter)
1921
+ return True
1922
+ except (StopIteration, GithubException):
1923
+ pass
1924
+
1925
+ except GithubException:
1926
+ pass
1927
+
1928
+ return False
1929
+
1930
+ # Monitors activity of the specified GitHub user
1811
1931
  def github_monitor_user(user, csv_file_name):
1812
1932
 
1813
1933
  try:
@@ -1824,6 +1944,10 @@ def github_monitor_user(user, csv_file_name):
1824
1944
  events = []
1825
1945
  repos_list = []
1826
1946
  event_date: datetime | None = None
1947
+ blocked = None
1948
+ public = False
1949
+
1950
+ print("Sneaking into GitHub like a ninja ...")
1827
1951
 
1828
1952
  try:
1829
1953
  auth = Auth.Token(GITHUB_TOKEN)
@@ -1856,12 +1980,15 @@ def github_monitor_user(user, csv_file_name):
1856
1980
  starred_list = g_user.get_starred()
1857
1981
  starred_count = starred_list.totalCount
1858
1982
 
1983
+ public = is_profile_public(g, user)
1984
+ blocked = is_blocked_by(user) if public else None
1985
+
1859
1986
  if not DO_NOT_MONITOR_GITHUB_EVENTS:
1860
1987
  events = list(islice(g_user.get_events(), EVENTS_NUMBER))
1861
1988
  available_events = len(events)
1862
1989
 
1863
1990
  except Exception as e:
1864
- print(f"* Error: {e}")
1991
+ print(f"\n* Error: {e}")
1865
1992
  sys.exit(1)
1866
1993
 
1867
1994
  last_event_id = 0
@@ -1879,7 +2006,7 @@ def github_monitor_user(user, csv_file_name):
1879
2006
  if last_event_id:
1880
2007
  last_event_ts = newest.created_at
1881
2008
  except Exception as e:
1882
- print(f"* Cannot get event IDs / timestamps: {e}\n")
2009
+ print(f"\n* Cannot get event IDs / timestamps: {e}\n")
1883
2010
  pass
1884
2011
 
1885
2012
  followers_old_count = followers_count
@@ -1893,6 +2020,8 @@ def github_monitor_user(user, csv_file_name):
1893
2020
  company_old = company
1894
2021
  email_old = email
1895
2022
  blog_old = blog
2023
+ blocked_old = blocked
2024
+ public_old = public
1896
2025
 
1897
2026
  last_event_id_old = last_event_id
1898
2027
  last_event_ts_old = last_event_ts
@@ -1902,7 +2031,7 @@ def github_monitor_user(user, csv_file_name):
1902
2031
  if user_myself_name:
1903
2032
  user_myself_name_str += f" ({user_myself_name})"
1904
2033
 
1905
- print(f"Token belongs to:\t\t{user_myself_name_str}" + f"\n\t\t\t\t[ {user_myself_url} ]" if user_myself_url else "")
2034
+ print(f"\nToken belongs to:\t\t{user_myself_name_str}" + f"\n\t\t\t\t[ {user_myself_url} ]" if user_myself_url else "")
1906
2035
 
1907
2036
  user_name_str = user_login
1908
2037
  if user_name:
@@ -1923,6 +2052,9 @@ def github_monitor_user(user, csv_file_name):
1923
2052
  if blog:
1924
2053
  print(f"Blog URL:\t\t\t{blog}")
1925
2054
 
2055
+ print(f"\nPublic profile:\t\t\t{'Yes' if public else 'No'}")
2056
+ print(f"Blocked by the user:\t\t{'Unknown' if blocked is None else ('Yes' if blocked else 'No')}")
2057
+
1926
2058
  print(f"\nAccount creation date:\t\t{get_date_from_ts(account_created_date)} ({calculate_timespan(int(time.time()), account_created_date, show_seconds=False)} ago)")
1927
2059
  print(f"Account updated date:\t\t{get_date_from_ts(account_updated_date)} ({calculate_timespan(int(time.time()), account_updated_date, show_seconds=False)} ago)")
1928
2060
  account_updated_date_old = account_updated_date
@@ -1931,6 +2063,7 @@ def github_monitor_user(user, csv_file_name):
1931
2063
  print(f"Followings:\t\t\t{followings_count}")
1932
2064
  print(f"Repositories:\t\t\t{repos_count}")
1933
2065
  print(f"Starred repos:\t\t\t{starred_count}")
2066
+
1934
2067
  if not DO_NOT_MONITOR_GITHUB_EVENTS:
1935
2068
  print(f"Available events:\t\t{available_events}{'+' if available_events == EVENTS_NUMBER else ''}")
1936
2069
 
@@ -1983,7 +2116,7 @@ def github_monitor_user(user, csv_file_name):
1983
2116
  alive_counter = 0
1984
2117
  email_sent = False
1985
2118
 
1986
- # main loop
2119
+ # Primary loop
1987
2120
  while True:
1988
2121
 
1989
2122
  try:
@@ -2056,8 +2189,8 @@ def github_monitor_user(user, csv_file_name):
2056
2189
  except Exception as e:
2057
2190
  print(f"* Error: {e}")
2058
2191
 
2059
- m_subject = f"Github user {user} bio has changed!"
2060
- m_body = f"Github user {user} bio has changed\n\nOld bio:\n\n{bio_old}\n\nNew bio:\n\n{bio}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2192
+ m_subject = f"GitHub user {user} bio has changed!"
2193
+ m_body = f"GitHub user {user} bio has changed\n\nOld bio:\n\n{bio_old}\n\nNew bio:\n\n{bio}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2061
2194
 
2062
2195
  if PROFILE_NOTIFICATION:
2063
2196
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2080,8 +2213,8 @@ def github_monitor_user(user, csv_file_name):
2080
2213
  except Exception as e:
2081
2214
  print(f"* Error: {e}")
2082
2215
 
2083
- m_subject = f"Github user {user} location has changed!"
2084
- m_body = f"Github user {user} location has changed\n\nOld location: {location_old}\n\nNew location: {location}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2216
+ m_subject = f"GitHub user {user} location has changed!"
2217
+ m_body = f"GitHub user {user} location has changed\n\nOld location: {location_old}\n\nNew location: {location}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2085
2218
 
2086
2219
  if PROFILE_NOTIFICATION:
2087
2220
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2104,8 +2237,8 @@ def github_monitor_user(user, csv_file_name):
2104
2237
  except Exception as e:
2105
2238
  print(f"* Error: {e}")
2106
2239
 
2107
- m_subject = f"Github user {user} name has changed!"
2108
- m_body = f"Github user {user} name has changed\n\nOld user name: {user_name_old}\n\nNew user name: {user_name}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2240
+ m_subject = f"GitHub user {user} name has changed!"
2241
+ m_body = f"GitHub user {user} name has changed\n\nOld user name: {user_name_old}\n\nNew user name: {user_name}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2109
2242
 
2110
2243
  if PROFILE_NOTIFICATION:
2111
2244
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2128,8 +2261,8 @@ def github_monitor_user(user, csv_file_name):
2128
2261
  except Exception as e:
2129
2262
  print(f"* Error: {e}")
2130
2263
 
2131
- m_subject = f"Github user {user} company has changed!"
2132
- m_body = f"Github user {user} company has changed\n\nOld company: {company_old}\n\nNew company: {company}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2264
+ m_subject = f"GitHub user {user} company has changed!"
2265
+ m_body = f"GitHub user {user} company has changed\n\nOld company: {company_old}\n\nNew company: {company}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2133
2266
 
2134
2267
  if PROFILE_NOTIFICATION:
2135
2268
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2152,8 +2285,8 @@ def github_monitor_user(user, csv_file_name):
2152
2285
  except Exception as e:
2153
2286
  print(f"* Error: {e}")
2154
2287
 
2155
- m_subject = f"Github user {user} email has changed!"
2156
- m_body = f"Github user {user} email has changed\n\nOld email: {email_old}\n\nNew email: {email}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2288
+ m_subject = f"GitHub user {user} email has changed!"
2289
+ m_body = f"GitHub user {user} email has changed\n\nOld email: {email_old}\n\nNew email: {email}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2157
2290
 
2158
2291
  if PROFILE_NOTIFICATION:
2159
2292
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2176,8 +2309,8 @@ def github_monitor_user(user, csv_file_name):
2176
2309
  except Exception as e:
2177
2310
  print(f"* Error: {e}")
2178
2311
 
2179
- m_subject = f"Github user {user} blog URL has changed!"
2180
- m_body = f"Github user {user} blog URL has changed\n\nOld blog URL: {blog_old}\n\nNew blog URL: {blog}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2312
+ m_subject = f"GitHub user {user} blog URL has changed!"
2313
+ m_body = f"GitHub user {user} blog URL has changed\n\nOld blog URL: {blog_old}\n\nNew blog URL: {blog}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2181
2314
 
2182
2315
  if PROFILE_NOTIFICATION:
2183
2316
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2200,8 +2333,8 @@ def github_monitor_user(user, csv_file_name):
2200
2333
  except Exception as e:
2201
2334
  print(f"* Error: {e}")
2202
2335
 
2203
- m_subject = f"Github user {user} account has been updated! (after {calculate_timespan(account_updated_date, account_updated_date_old, show_seconds=False, granularity=2)})"
2204
- m_body = f"Github user {user} account has been updated (after {calculate_timespan(account_updated_date, account_updated_date_old, show_seconds=False, granularity=2)})\n\nOld account update date: {get_date_from_ts(account_updated_date_old)}\n\nNew account update date: {get_date_from_ts(account_updated_date)}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2336
+ m_subject = f"GitHub user {user} account has been updated! (after {calculate_timespan(account_updated_date, account_updated_date_old, show_seconds=False, granularity=2)})"
2337
+ m_body = f"GitHub user {user} account has been updated (after {calculate_timespan(account_updated_date, account_updated_date_old, show_seconds=False, granularity=2)})\n\nOld account update date: {get_date_from_ts(account_updated_date_old)}\n\nNew account update date: {get_date_from_ts(account_updated_date)}\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2205
2338
 
2206
2339
  if PROFILE_NOTIFICATION:
2207
2340
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2211,6 +2344,62 @@ def github_monitor_user(user, csv_file_name):
2211
2344
  print(f"Check interval:\t\t\t{display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)})")
2212
2345
  print_cur_ts("Timestamp:\t\t\t")
2213
2346
 
2347
+ # Profile visibility changed
2348
+ public = is_profile_public(g, user)
2349
+ if public != public_old:
2350
+
2351
+ def _get_profile_status(public):
2352
+ return "public" if public else "private"
2353
+
2354
+ print(f"* User {user} has changed profile visibility to '{_get_profile_status(public)}' !\n")
2355
+
2356
+ try:
2357
+ if csv_file_name:
2358
+ write_csv_entry(csv_file_name, now_local_naive(), "Profile Visibility", user, _get_profile_status(public_old), _get_profile_status(public))
2359
+ except Exception as e:
2360
+ print(f"* Error: {e}")
2361
+
2362
+ m_subject = f"GitHub user {user} has changed profile visibility to '{_get_profile_status(public)}' !"
2363
+ m_body = f"GitHub user {user} has changed profile visibility to '{_get_profile_status(public)}' !\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2364
+
2365
+ if PROFILE_NOTIFICATION:
2366
+ print(f"Sending email notification to {RECEIVER_EMAIL}")
2367
+ send_email(m_subject, m_body, "", SMTP_SSL)
2368
+
2369
+ public_old = public
2370
+ print(f"Check interval:\t\t\t{display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)})")
2371
+ print_cur_ts("Timestamp:\t\t\t")
2372
+
2373
+ # Blocked status changed
2374
+ blocked = is_blocked_by(user) if public else None
2375
+
2376
+ if blocked is not None and blocked_old is None:
2377
+ blocked_old = blocked
2378
+
2379
+ elif None not in (blocked_old, blocked) and blocked != blocked_old:
2380
+
2381
+ def _get_blocked_status(blocked, public):
2382
+ return 'Unknown' if blocked is None else ('Yes' if blocked else 'No')
2383
+
2384
+ print(f"* User {user} has {'blocked' if blocked else 'unblocked'} you!\n")
2385
+
2386
+ try:
2387
+ if csv_file_name:
2388
+ write_csv_entry(csv_file_name, now_local_naive(), "Block Status", user, _get_blocked_status(blocked_old, public), _get_blocked_status(blocked, public))
2389
+ except Exception as e:
2390
+ print(f"* Error: {e}")
2391
+
2392
+ m_subject = f"GitHub user {user} has {'blocked' if blocked else 'unblocked'} you!"
2393
+ m_body = f"GitHub user {user} has {'blocked' if blocked else 'unblocked'} you!\n\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2394
+
2395
+ if PROFILE_NOTIFICATION:
2396
+ print(f"Sending email notification to {RECEIVER_EMAIL}")
2397
+ send_email(m_subject, m_body, "", SMTP_SSL)
2398
+
2399
+ blocked_old = blocked
2400
+ print(f"Check interval:\t\t\t{display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)})")
2401
+ print_cur_ts("Timestamp:\t\t\t")
2402
+
2214
2403
  list_of_repos = []
2215
2404
 
2216
2405
  # Changed repos details
@@ -2269,7 +2458,7 @@ def github_monitor_user(user, csv_file_name):
2269
2458
  write_csv_entry(csv_file_name, now_local_naive(), "Repo Update Date", r_name, convert_to_local_naive(r_update_old), convert_to_local_naive(r_update))
2270
2459
  except Exception as e:
2271
2460
  print(f"* Error: {e}")
2272
- m_subject = f"Github user {user} repo '{r_name}' update date has changed ! (after {calculate_timespan(r_update, r_update_old, show_seconds=False, granularity=2)})"
2461
+ m_subject = f"GitHub user {user} repo '{r_name}' update date has changed ! (after {calculate_timespan(r_update, r_update_old, show_seconds=False, granularity=2)})"
2273
2462
  m_body = f"{r_message}\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2274
2463
  if REPO_UPDATE_DATE_NOTIFICATION:
2275
2464
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2301,7 +2490,7 @@ def github_monitor_user(user, csv_file_name):
2301
2490
  write_csv_entry(csv_file_name, now_local_naive(), "Repo Description", r_name, r_descr_old, r_descr)
2302
2491
  except Exception as e:
2303
2492
  print(f"* Error: {e}")
2304
- m_subject = f"Github user {user} repo '{r_name}' description has changed !"
2493
+ m_subject = f"GitHub user {user} repo '{r_name}' description has changed !"
2305
2494
  m_body = f"{r_message}\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2306
2495
  if REPO_NOTIFICATION:
2307
2496
  print(f"Sending email notification to {RECEIVER_EMAIL}")
@@ -2311,7 +2500,7 @@ def github_monitor_user(user, csv_file_name):
2311
2500
 
2312
2501
  list_of_repos_old = list_of_repos
2313
2502
 
2314
- # New Github events
2503
+ # New GitHub events
2315
2504
  if not DO_NOT_MONITOR_GITHUB_EVENTS:
2316
2505
  events = list(gh_call(lambda: list(islice(g_user.get_events(), EVENTS_NUMBER)))())
2317
2506
  if events is not None:
@@ -2366,8 +2555,8 @@ def github_monitor_user(user, csv_file_name):
2366
2555
  except Exception as e:
2367
2556
  print(f"* Error: {e}")
2368
2557
 
2369
- m_subject = f"Github user {user} has new {event.type} (repo: {repo_name})"
2370
- m_body = f"Github user {user} has new {event.type} event\n\n{event_text}\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2558
+ m_subject = f"GitHub user {user} has new {event.type} (repo: {repo_name})"
2559
+ m_body = f"GitHub user {user} has new {event.type} event\n\n{event_text}\nCheck interval: {display_time(GITHUB_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - GITHUB_CHECK_INTERVAL, int(time.time()), short=True)}){get_cur_ts(nl_ch + 'Timestamp: ')}"
2371
2560
 
2372
2561
  if EVENT_NOTIFICATION:
2373
2562
  print(f"\nSending email notification to {RECEIVER_EMAIL}")
@@ -2409,7 +2598,7 @@ def main():
2409
2598
 
2410
2599
  clear_screen(CLEAR_SCREEN)
2411
2600
 
2412
- print(f"Github Monitoring Tool v{VERSION}\n")
2601
+ print(f"GitHub Monitoring Tool v{VERSION}\n")
2413
2602
 
2414
2603
  parser = argparse.ArgumentParser(
2415
2604
  prog="github_monitor",
@@ -2800,11 +2989,11 @@ def main():
2800
2989
  REPO_UPDATE_DATE_NOTIFICATION = False
2801
2990
  ERROR_NOTIFICATION = False
2802
2991
 
2803
- print(f"* Github polling interval:\t[ {display_time(GITHUB_CHECK_INTERVAL)} ]")
2992
+ print(f"* GitHub polling interval:\t[ {display_time(GITHUB_CHECK_INTERVAL)} ]")
2804
2993
  print(f"* Email notifications:\t\t[profile changes = {PROFILE_NOTIFICATION}] [new events = {EVENT_NOTIFICATION}]\n*\t\t\t\t[repos changes = {REPO_NOTIFICATION}] [repos update date = {REPO_UPDATE_DATE_NOTIFICATION}]\n*\t\t\t\t[errors = {ERROR_NOTIFICATION}]")
2805
- print(f"* Github API URL:\t\t{GITHUB_API_URL}")
2994
+ print(f"* GitHub API URL:\t\t{GITHUB_API_URL}")
2806
2995
  print(f"* Track repos changes:\t\t{TRACK_REPOS_CHANGES}")
2807
- print(f"* Monitor Github events:\t{not DO_NOT_MONITOR_GITHUB_EVENTS}")
2996
+ print(f"* Monitor GitHub events:\t{not DO_NOT_MONITOR_GITHUB_EVENTS}")
2808
2997
  print(f"* Liveness check:\t\t{bool(LIVENESS_CHECK_INTERVAL)}" + (f" ({display_time(LIVENESS_CHECK_INTERVAL)})" if LIVENESS_CHECK_INTERVAL else ""))
2809
2998
  print(f"* CSV logging enabled:\t\t{bool(CSV_FILE)}" + (f" ({CSV_FILE})" if CSV_FILE else ""))
2810
2999
  print(f"* Output logging enabled:\t{not DISABLE_LOGGING}" + (f" ({FINAL_LOG_PATH})" if not DISABLE_LOGGING else ""))
@@ -2812,7 +3001,7 @@ def main():
2812
3001
  print(f"* Dotenv file:\t\t\t{env_path or 'None'}")
2813
3002
  print(f"* Local timezone:\t\t{LOCAL_TIMEZONE}")
2814
3003
 
2815
- out = f"\nMonitoring Github user {args.username}"
3004
+ out = f"\nMonitoring GitHub user {args.username}"
2816
3005
  print(out)
2817
3006
  print("-" * len(out))
2818
3007
 
@@ -1,7 +0,0 @@
1
- github_monitor.py,sha256=F1MAvwW8lwzUKCKUlV4Xexs8OW5qjDxsAYN43XeDQew,113740
2
- github_monitor-1.9.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
3
- github_monitor-1.9.1.dist-info/METADATA,sha256=u3ake7RKz2uzVPbl2KAkYew8ouT8_sD9GiuFzZe4_Ak,16541
4
- github_monitor-1.9.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
- github_monitor-1.9.1.dist-info/entry_points.txt,sha256=hV03y00u1L16S5BwBSLQvFsZcL2WGRtjzlrmu9U9SN0,55
6
- github_monitor-1.9.1.dist-info/top_level.txt,sha256=HDN2988ydvH9JZT32PushzqrcD05Q5qg960vgHGIaI8,15
7
- github_monitor-1.9.1.dist-info/RECORD,,