github-monitor 2.1__py3-none-any.whl → 2.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.
- {github_monitor-2.1.dist-info → github_monitor-2.2.1.dist-info}/METADATA +22 -5
- github_monitor-2.2.1.dist-info/RECORD +7 -0
- github_monitor.py +340 -22
- github_monitor-2.1.dist-info/RECORD +0 -7
- {github_monitor-2.1.dist-info → github_monitor-2.2.1.dist-info}/WHEEL +0 -0
- {github_monitor-2.1.dist-info → github_monitor-2.2.1.dist-info}/entry_points.txt +0 -0
- {github_monitor-2.1.dist-info → github_monitor-2.2.1.dist-info}/licenses/LICENSE +0 -0
- {github_monitor-2.1.dist-info → github_monitor-2.2.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github_monitor
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.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
|
|
@@ -39,6 +39,7 @@ OSINT tool for real-time monitoring of GitHub users' activities, including profi
|
|
|
39
39
|
- added/removed public repositories
|
|
40
40
|
- changes in user name, email, location, company, bio and blog URL
|
|
41
41
|
- changes in profile visibility (public to private and vice versa)
|
|
42
|
+
- changes in user's daily contributions
|
|
42
43
|
- detection when a user blocks or unblocks you
|
|
43
44
|
- detection of account metadata changes (such as account update date)
|
|
44
45
|
- Email notifications for different events (new GitHub events, changed followings, followers, repositories, user name, email, location, company, bio, blog URL etc.)
|
|
@@ -86,8 +87,8 @@ OSINT tool for real-time monitoring of GitHub users' activities, including profi
|
|
|
86
87
|
|
|
87
88
|
Tested on:
|
|
88
89
|
|
|
89
|
-
* **macOS**: Ventura, Sonoma, Sequoia
|
|
90
|
-
* **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
|
|
90
|
+
* **macOS**: Ventura, Sonoma, Sequoia, Tahoe
|
|
91
|
+
* **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
|
|
91
92
|
* **Windows**: 10, 11
|
|
92
93
|
|
|
93
94
|
It should work on other versions of macOS, Linux, Unix and Windows as well.
|
|
@@ -298,6 +299,12 @@ By default, only user-owned repos are tracked. To include forks and collaboratio
|
|
|
298
299
|
github_monitor github_username -j -a
|
|
299
300
|
```
|
|
300
301
|
|
|
302
|
+
If you want to track user's daily contributions then use the `-m` flag:
|
|
303
|
+
|
|
304
|
+
```sh
|
|
305
|
+
github_monitor github_username -m
|
|
306
|
+
```
|
|
307
|
+
|
|
301
308
|
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:
|
|
302
309
|
|
|
303
310
|
```sh
|
|
@@ -379,7 +386,7 @@ To get email notifications when changes in user repositories are detected (e.g.
|
|
|
379
386
|
- or use the `-q` flag
|
|
380
387
|
|
|
381
388
|
```sh
|
|
382
|
-
github_monitor github_username -q
|
|
389
|
+
github_monitor github_username -j -q
|
|
383
390
|
```
|
|
384
391
|
|
|
385
392
|
To be informed whenever changes in the update date of user repositories are detected:
|
|
@@ -387,11 +394,21 @@ To be informed whenever changes in the update date of user repositories are dete
|
|
|
387
394
|
- or use the `-u` flag
|
|
388
395
|
|
|
389
396
|
```sh
|
|
390
|
-
github_monitor github_username -u
|
|
397
|
+
github_monitor github_username -j -u
|
|
391
398
|
```
|
|
392
399
|
|
|
393
400
|
The last two options (`-q` and `-u`) only work if tracking of repositories changes is enabled (`-j`).
|
|
394
401
|
|
|
402
|
+
To be informed about user's daily contributions:
|
|
403
|
+
- set `CONTRIB_NOTIFICATION` to `True`
|
|
404
|
+
- or use the `-y` flag
|
|
405
|
+
|
|
406
|
+
```sh
|
|
407
|
+
github_monitor github_username -m -y
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The `-y` only works if tracking of daily contributions is enabled (`-m`).
|
|
411
|
+
|
|
395
412
|
To disable sending an email on errors (enabled by default):
|
|
396
413
|
- set `ERROR_NOTIFICATION` to `False`
|
|
397
414
|
- or use the `-e` flag
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
github_monitor.py,sha256=y-KCw1m1Z_ck3dy81Wu0WGtmJY5wJXRtOKJhaVFp-rI,138012
|
|
2
|
+
github_monitor-2.2.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
3
|
+
github_monitor-2.2.1.dist-info/METADATA,sha256=TBFgooDz_els2Fgm4wMNoP1dcNMirFtq61DnbGAi6ls,17461
|
|
4
|
+
github_monitor-2.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
+
github_monitor-2.2.1.dist-info/entry_points.txt,sha256=hV03y00u1L16S5BwBSLQvFsZcL2WGRtjzlrmu9U9SN0,55
|
|
6
|
+
github_monitor-2.2.1.dist-info/top_level.txt,sha256=HDN2988ydvH9JZT32PushzqrcD05Q5qg960vgHGIaI8,15
|
|
7
|
+
github_monitor-2.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.1
|
|
4
|
+
v2.2.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.1"
|
|
19
|
+
VERSION = "2.2.1"
|
|
20
20
|
|
|
21
21
|
# ---------------------------
|
|
22
22
|
# CONFIGURATION SECTION START
|
|
@@ -76,6 +76,7 @@ EVENT_NOTIFICATION = False
|
|
|
76
76
|
|
|
77
77
|
# Whether to send an email when user's repositories change (stargazers, watchers, forks, issues,
|
|
78
78
|
# PRs, description etc., except for update date)
|
|
79
|
+
# Requires TRACK_REPOS_CHANGES to be enabled
|
|
79
80
|
# Can also be enabled via the -q flag
|
|
80
81
|
REPO_NOTIFICATION = False
|
|
81
82
|
|
|
@@ -83,6 +84,11 @@ REPO_NOTIFICATION = False
|
|
|
83
84
|
# Can also be enabled via the -u flag
|
|
84
85
|
REPO_UPDATE_DATE_NOTIFICATION = False
|
|
85
86
|
|
|
87
|
+
# Whether to send an email when user's daily contributions count changes
|
|
88
|
+
# Requires TRACK_CONTRIB_CHANGES to be enabled
|
|
89
|
+
# Can also be enabled via the -y flag
|
|
90
|
+
CONTRIB_NOTIFICATION = False
|
|
91
|
+
|
|
86
92
|
# Whether to send an email on errors
|
|
87
93
|
# Can also be disabled via the -e flag
|
|
88
94
|
ERROR_NOTIFICATION = True
|
|
@@ -126,6 +132,14 @@ EVENTS_TO_MONITOR = [
|
|
|
126
132
|
# any events older than the most recent EVENTS_NUMBER will be missed
|
|
127
133
|
EVENTS_NUMBER = 30 # 1 page
|
|
128
134
|
|
|
135
|
+
# If True, track user's repository changes (changed stargazers, watchers, forks, description, update date etc.)
|
|
136
|
+
# Can also be enabled using the -j flag
|
|
137
|
+
TRACK_REPOS_CHANGES = False
|
|
138
|
+
|
|
139
|
+
# If True, disable event monitoring
|
|
140
|
+
# Can also be disabled using the -k flag
|
|
141
|
+
DO_NOT_MONITOR_GITHUB_EVENTS = False
|
|
142
|
+
|
|
129
143
|
# If True, fetch all user repos (owned, forks, collaborations); otherwise, fetch only owned repos
|
|
130
144
|
GET_ALL_REPOS = False
|
|
131
145
|
|
|
@@ -133,6 +147,10 @@ GET_ALL_REPOS = False
|
|
|
133
147
|
# In listing mode (-r), blocked repos are always shown
|
|
134
148
|
BLOCKED_REPOS = False
|
|
135
149
|
|
|
150
|
+
# If True, track and log user's daily contributions count changes
|
|
151
|
+
# Can also be enabled using the -m flag
|
|
152
|
+
TRACK_CONTRIB_CHANGES = False
|
|
153
|
+
|
|
136
154
|
# How often to print a "liveness check" message to the output; in seconds
|
|
137
155
|
# Set to 0 to disable
|
|
138
156
|
LIVENESS_CHECK_INTERVAL = 43200 # 12 hours
|
|
@@ -200,13 +218,17 @@ PROFILE_NOTIFICATION = False
|
|
|
200
218
|
EVENT_NOTIFICATION = False
|
|
201
219
|
REPO_NOTIFICATION = False
|
|
202
220
|
REPO_UPDATE_DATE_NOTIFICATION = False
|
|
221
|
+
CONTRIB_NOTIFICATION = False
|
|
203
222
|
ERROR_NOTIFICATION = False
|
|
204
223
|
GITHUB_CHECK_INTERVAL = 0
|
|
205
224
|
LOCAL_TIMEZONE = ""
|
|
206
225
|
EVENTS_TO_MONITOR = []
|
|
207
226
|
EVENTS_NUMBER = 0
|
|
227
|
+
TRACK_REPOS_CHANGES = False
|
|
228
|
+
DO_NOT_MONITOR_GITHUB_EVENTS = False
|
|
208
229
|
GET_ALL_REPOS = False
|
|
209
230
|
BLOCKED_REPOS = False
|
|
231
|
+
TRACK_CONTRIB_CHANGES = False
|
|
210
232
|
LIVENESS_CHECK_INTERVAL = 0
|
|
211
233
|
CHECK_INTERNET_URL = ""
|
|
212
234
|
CHECK_INTERNET_TIMEOUT = 0
|
|
@@ -234,9 +256,6 @@ LIVENESS_CHECK_COUNTER = LIVENESS_CHECK_INTERVAL / GITHUB_CHECK_INTERVAL
|
|
|
234
256
|
stdout_bck = None
|
|
235
257
|
csvfieldnames = ['Date', 'Type', 'Name', 'Old', 'New']
|
|
236
258
|
|
|
237
|
-
TRACK_REPOS_CHANGES = False
|
|
238
|
-
DO_NOT_MONITOR_GITHUB_EVENTS = False
|
|
239
|
-
|
|
240
259
|
CLI_CONFIG_PATH = None
|
|
241
260
|
|
|
242
261
|
# to solve the issue: 'SyntaxError: f-string expression part cannot include a backslash'
|
|
@@ -252,7 +271,7 @@ if sys.version_info < (3, 10):
|
|
|
252
271
|
import time
|
|
253
272
|
import string
|
|
254
273
|
import os
|
|
255
|
-
from datetime import datetime, timezone
|
|
274
|
+
from datetime import datetime, timezone, date
|
|
256
275
|
from dateutil import relativedelta
|
|
257
276
|
from dateutil.parser import isoparse
|
|
258
277
|
import calendar
|
|
@@ -278,6 +297,7 @@ import re
|
|
|
278
297
|
import ipaddress
|
|
279
298
|
try:
|
|
280
299
|
from github import Github, Auth, GithubException, UnknownObjectException
|
|
300
|
+
from github.GithubException import RateLimitExceededException
|
|
281
301
|
from github.GithubException import BadCredentialsException
|
|
282
302
|
except ModuleNotFoundError:
|
|
283
303
|
raise SystemExit("Error: Couldn't find the PyGitHub library !\n\nTo install it, run:\n pip3 install PyGithub\n\nOnce installed, re-run this tool. For more help, visit:\nhttps://github.com/PyGithub/PyGithub")
|
|
@@ -288,7 +308,9 @@ import socket
|
|
|
288
308
|
from typing import Any, Callable
|
|
289
309
|
import shutil
|
|
290
310
|
from pathlib import Path
|
|
291
|
-
|
|
311
|
+
from typing import Optional
|
|
312
|
+
import datetime as dt
|
|
313
|
+
import requests
|
|
292
314
|
|
|
293
315
|
NET_ERRORS = (
|
|
294
316
|
req.exceptions.RequestException,
|
|
@@ -566,6 +588,11 @@ def now_local_naive():
|
|
|
566
588
|
return datetime.now(pytz.timezone(LOCAL_TIMEZONE)).replace(microsecond=0, tzinfo=None)
|
|
567
589
|
|
|
568
590
|
|
|
591
|
+
# Returns today's date in LOCAL_TIMEZONE (naive date)
|
|
592
|
+
def today_local() -> dt.date:
|
|
593
|
+
return now_local_naive().date()
|
|
594
|
+
|
|
595
|
+
|
|
569
596
|
# Returns the current date/time in human readable format; eg. Sun 21 Apr 2024, 15:08:45
|
|
570
597
|
def get_cur_ts(ts_str=""):
|
|
571
598
|
return (f'{ts_str}{calendar.day_abbr[(now_local_naive()).weekday()]}, {now_local_naive().strftime("%d %b %Y, %H:%M:%S")}')
|
|
@@ -635,6 +662,11 @@ def get_short_date_from_ts(ts, show_year=False, show_hour=True, show_weekday=Tru
|
|
|
635
662
|
ts_rounded = int(round(ts))
|
|
636
663
|
ts_new = datetime.fromtimestamp(ts_rounded, tz)
|
|
637
664
|
|
|
665
|
+
elif isinstance(ts, date):
|
|
666
|
+
ts = datetime.combine(ts, datetime.min.time())
|
|
667
|
+
ts = pytz.utc.localize(ts)
|
|
668
|
+
ts_new = ts.astimezone(tz)
|
|
669
|
+
|
|
638
670
|
else:
|
|
639
671
|
return ""
|
|
640
672
|
|
|
@@ -840,6 +872,34 @@ def gh_call(fn: Callable[..., Any], retries=NET_MAX_RETRIES, backoff=NET_BASE_BA
|
|
|
840
872
|
for i in range(1, retries + 1):
|
|
841
873
|
try:
|
|
842
874
|
return fn(*args, **kwargs)
|
|
875
|
+
except RateLimitExceededException as e:
|
|
876
|
+
headers = getattr(e, "headers", None)
|
|
877
|
+
|
|
878
|
+
reset_str = None
|
|
879
|
+
if headers:
|
|
880
|
+
val = headers.get("X-RateLimit-Reset")
|
|
881
|
+
if isinstance(val, str):
|
|
882
|
+
reset_str = val
|
|
883
|
+
|
|
884
|
+
sleep_for: int
|
|
885
|
+
if reset_str is not None and reset_str.isdigit():
|
|
886
|
+
reset_epoch = int(reset_str)
|
|
887
|
+
sleep_for = max(0, reset_epoch - int(time.time()) + 1)
|
|
888
|
+
else:
|
|
889
|
+
retry_after_str = None
|
|
890
|
+
if headers:
|
|
891
|
+
ra = headers.get("Retry-After")
|
|
892
|
+
if isinstance(ra, str):
|
|
893
|
+
retry_after_str = ra
|
|
894
|
+
if retry_after_str is not None and retry_after_str.isdigit():
|
|
895
|
+
sleep_for = int(retry_after_str)
|
|
896
|
+
else:
|
|
897
|
+
sleep_for = int(backoff * i)
|
|
898
|
+
|
|
899
|
+
print(f"* {fn.__name__} rate limited, sleeping {sleep_for}s (retry {i}/{retries})")
|
|
900
|
+
time.sleep(sleep_for)
|
|
901
|
+
continue
|
|
902
|
+
|
|
843
903
|
except NET_ERRORS as e:
|
|
844
904
|
print(f"* {fn.__name__} error: {e} (retry {i}/{retries})")
|
|
845
905
|
time.sleep(backoff * i)
|
|
@@ -1138,6 +1198,13 @@ def format_body_block(content, indent=" "):
|
|
|
1138
1198
|
return f"\n{indented}"
|
|
1139
1199
|
|
|
1140
1200
|
|
|
1201
|
+
# Returns the base web URL for GitHub or GHE (e.g. https://github.com or https://ghe.example.com)
|
|
1202
|
+
def github_web_base() -> str:
|
|
1203
|
+
if "api.github.com" in GITHUB_API_URL:
|
|
1204
|
+
return "https://github.com"
|
|
1205
|
+
return GITHUB_API_URL.replace("/api/v3", "").rstrip("/")
|
|
1206
|
+
|
|
1207
|
+
|
|
1141
1208
|
# Prints details about passed GitHub event
|
|
1142
1209
|
def github_print_event(event, g, time_passed=False, ts: datetime | None = None):
|
|
1143
1210
|
|
|
@@ -1158,19 +1225,33 @@ def github_print_event(event, g, time_passed=False, ts: datetime | None = None):
|
|
|
1158
1225
|
st += print_v(f"Event type:\t\t\t{event.type}")
|
|
1159
1226
|
|
|
1160
1227
|
if event.repo.id:
|
|
1161
|
-
repo_name = event.repo.name
|
|
1162
|
-
repo_url = event.repo.url.replace("https://api.github.com/repos/", "https://github.com/")
|
|
1163
|
-
st += print_v(f"\nRepo name:\t\t\t{repo_name}")
|
|
1164
|
-
st += print_v(f"Repo URL:\t\t\t{repo_url}")
|
|
1165
|
-
|
|
1166
1228
|
try:
|
|
1167
1229
|
desc_len = 80
|
|
1168
1230
|
repo = g.get_repo(event.repo.name)
|
|
1169
|
-
|
|
1231
|
+
|
|
1232
|
+
# For ForkEvent, prefer the source repo if available
|
|
1233
|
+
if event.type == "ForkEvent" and repo is not None:
|
|
1234
|
+
try:
|
|
1235
|
+
parent = gh_call(lambda: getattr(repo, "parent", None))()
|
|
1236
|
+
if parent:
|
|
1237
|
+
repo = parent
|
|
1238
|
+
except Exception:
|
|
1239
|
+
pass
|
|
1240
|
+
|
|
1241
|
+
repo_name = getattr(repo, "full_name", event.repo.name)
|
|
1242
|
+
|
|
1243
|
+
api_prefix = GITHUB_API_URL.rstrip("/") + "/repos/"
|
|
1244
|
+
repo_url = getattr(repo, "html_url", event.repo.url.replace(api_prefix, github_web_base() + "/"))
|
|
1245
|
+
|
|
1246
|
+
st += print_v(f"\nRepo name:\t\t\t{repo_name}")
|
|
1247
|
+
st += print_v(f"Repo URL:\t\t\t{repo_url}")
|
|
1248
|
+
|
|
1249
|
+
desc = (repo.description or "") if repo else ""
|
|
1170
1250
|
cleaned = desc.replace('\n', ' ')
|
|
1171
1251
|
short_desc = cleaned[:desc_len] + '...' if len(cleaned) > desc_len else cleaned
|
|
1172
1252
|
if short_desc:
|
|
1173
1253
|
st += print_v(f"Repo description:\t\t{short_desc}")
|
|
1254
|
+
|
|
1174
1255
|
except UnknownObjectException:
|
|
1175
1256
|
repo = None
|
|
1176
1257
|
st += print_v("\nRepository not found or has been removed")
|
|
@@ -1198,6 +1279,7 @@ def github_print_event(event, g, time_passed=False, ts: datetime | None = None):
|
|
|
1198
1279
|
if event.payload.get("action"):
|
|
1199
1280
|
st += print_v(f"\nAction:\t\t\t\t{event.payload.get('action')}")
|
|
1200
1281
|
|
|
1282
|
+
# Prefer commits from payload when present (older API behavior)
|
|
1201
1283
|
if event.payload.get("commits"):
|
|
1202
1284
|
commits = event.payload["commits"]
|
|
1203
1285
|
commits_total = len(commits)
|
|
@@ -1208,7 +1290,7 @@ def github_print_event(event, g, time_passed=False, ts: datetime | None = None):
|
|
|
1208
1290
|
|
|
1209
1291
|
commit_details = None
|
|
1210
1292
|
if repo:
|
|
1211
|
-
commit_details = repo.get_commit(commit["sha"])
|
|
1293
|
+
commit_details = gh_call(lambda: repo.get_commit(commit["sha"]))()
|
|
1212
1294
|
|
|
1213
1295
|
if commit_details:
|
|
1214
1296
|
commit_date = commit_details.commit.author.date
|
|
@@ -1244,6 +1326,81 @@ def github_print_event(event, g, time_passed=False, ts: datetime | None = None):
|
|
|
1244
1326
|
st += print_v(f"\n - Commit message:\t\t'{commit['message']}'")
|
|
1245
1327
|
st += print_v("." * HORIZONTAL_LINE1)
|
|
1246
1328
|
|
|
1329
|
+
# Fallback for new Events API where PushEvent no longer includes commit summaries
|
|
1330
|
+
elif event.type == "PushEvent" and repo:
|
|
1331
|
+
before_sha = event.payload.get("before")
|
|
1332
|
+
head_sha = event.payload.get("head") or event.payload.get("after")
|
|
1333
|
+
size_hint = event.payload.get("size")
|
|
1334
|
+
|
|
1335
|
+
# Debug when payload has no commits
|
|
1336
|
+
# st += print_v("\n[debug] PushEvent payload has no 'commits' array; using compare API")
|
|
1337
|
+
# st += print_v(f"[debug] before:\t\t\t{before_sha}")
|
|
1338
|
+
# st += print_v(f"[debug] head/after:\t\t{head_sha}")
|
|
1339
|
+
# if size_hint is not None:
|
|
1340
|
+
# st += print_v(f"[debug] size (hint):\t\t{size_hint}")
|
|
1341
|
+
|
|
1342
|
+
if before_sha and head_sha and before_sha != head_sha:
|
|
1343
|
+
try:
|
|
1344
|
+
compare = gh_call(lambda: repo.compare(before_sha, head_sha))()
|
|
1345
|
+
except Exception as e:
|
|
1346
|
+
compare = None
|
|
1347
|
+
st += print_v(f"* Error using compare({before_sha[:12]}...{head_sha[:12]}): {e}")
|
|
1348
|
+
|
|
1349
|
+
if compare:
|
|
1350
|
+
commits = list(compare.commits)
|
|
1351
|
+
commits_total = len(commits)
|
|
1352
|
+
short_repo = getattr(repo, "full_name", repo_name)
|
|
1353
|
+
compare_url = f"{github_web_base()}/{short_repo}/compare/{before_sha[:12]}...{head_sha[:12]}"
|
|
1354
|
+
st += print_v(f"\nNumber of commits:\t\t{commits_total}")
|
|
1355
|
+
st += print_v(f"Compare URL:\t\t\t{compare_url}")
|
|
1356
|
+
|
|
1357
|
+
for commit_count, c in enumerate(commits, start=1):
|
|
1358
|
+
st += print_v(f"\n=== Commit {commit_count}/{commits_total} ===")
|
|
1359
|
+
st += print_v("." * HORIZONTAL_LINE1)
|
|
1360
|
+
|
|
1361
|
+
commit_sha = getattr(c, "sha", None) or getattr(c, "id", None)
|
|
1362
|
+
commit_details = gh_call(lambda: repo.get_commit(commit_sha))() if (repo and commit_sha) else None
|
|
1363
|
+
|
|
1364
|
+
if commit_details:
|
|
1365
|
+
commit_date = commit_details.commit.author.date
|
|
1366
|
+
st += print_v(f" - Commit date:\t\t\t{get_date_from_ts(commit_date)}")
|
|
1367
|
+
|
|
1368
|
+
if commit_sha:
|
|
1369
|
+
st += print_v(f" - Commit SHA:\t\t\t{commit_sha}")
|
|
1370
|
+
|
|
1371
|
+
author_name = None
|
|
1372
|
+
if commit_details and commit_details.commit and commit_details.commit.author:
|
|
1373
|
+
author_name = commit_details.commit.author.name
|
|
1374
|
+
st += print_v(f" - Commit author:\t\t{author_name or 'N/A'}")
|
|
1375
|
+
|
|
1376
|
+
if commit_details and commit_details.author:
|
|
1377
|
+
st += print_v(f" - Commit author URL:\t\t{commit_details.author.html_url}")
|
|
1378
|
+
|
|
1379
|
+
if commit_details:
|
|
1380
|
+
st += print_v(f" - Commit URL:\t\t\t{commit_details.html_url}")
|
|
1381
|
+
st += print_v(f" - Commit raw patch URL:\t{commit_details.html_url}.patch")
|
|
1382
|
+
|
|
1383
|
+
stats = getattr(commit_details, "stats", None)
|
|
1384
|
+
additions = stats.additions if stats else 0
|
|
1385
|
+
deletions = stats.deletions if stats else 0
|
|
1386
|
+
stats_total = stats.total if stats else 0
|
|
1387
|
+
st += print_v(f"\n - Additions/Deletions:\t\t+{additions} / -{deletions} ({stats_total})")
|
|
1388
|
+
|
|
1389
|
+
try:
|
|
1390
|
+
file_count = sum(1 for _ in commit_details.files)
|
|
1391
|
+
except Exception:
|
|
1392
|
+
file_count = "N/A"
|
|
1393
|
+
st += print_v(f" - Files changed:\t\t{file_count}")
|
|
1394
|
+
if file_count and file_count != "N/A":
|
|
1395
|
+
st += print_v(" - Changed files list:")
|
|
1396
|
+
for f in commit_details.files:
|
|
1397
|
+
st += print_v(f" • '{f.filename}' - {f.status} (+{f.additions} / -{f.deletions})")
|
|
1398
|
+
|
|
1399
|
+
st += print_v(f"\n - Commit message:\t\t'{commit_details.commit.message}'")
|
|
1400
|
+
st += print_v("." * HORIZONTAL_LINE1)
|
|
1401
|
+
else:
|
|
1402
|
+
st += print_v("\nNo compare range available (forced push, tag push, or identical before/after)")
|
|
1403
|
+
|
|
1247
1404
|
if event.payload.get("commits") == []:
|
|
1248
1405
|
st += print_v("\nNo new commits (forced push, tag push, branch reset or other ref update)")
|
|
1249
1406
|
|
|
@@ -1700,9 +1857,11 @@ def handle_profile_change(label, count_old, count_new, list_old, raw_list, user,
|
|
|
1700
1857
|
if removed_items:
|
|
1701
1858
|
print(f"Removed {label.lower()}:\n")
|
|
1702
1859
|
removed_mbody = f"\nRemoved {label.lower()}:\n\n"
|
|
1860
|
+
web_base = github_web_base()
|
|
1703
1861
|
for item in removed_items:
|
|
1704
|
-
item_url = (f"
|
|
1705
|
-
else f"
|
|
1862
|
+
item_url = (f"{web_base}/{item}/" if label.lower() in ["followers", "followings", "starred repos"]
|
|
1863
|
+
else f"{web_base}/{user}/{item}/")
|
|
1864
|
+
|
|
1706
1865
|
print(f"- {item} [ {item_url} ]")
|
|
1707
1866
|
removed_list_str += f"- {item} [ {item_url} ]\n"
|
|
1708
1867
|
try:
|
|
@@ -1715,9 +1874,10 @@ def handle_profile_change(label, count_old, count_new, list_old, raw_list, user,
|
|
|
1715
1874
|
if added_items:
|
|
1716
1875
|
print(f"Added {label.lower()}:\n")
|
|
1717
1876
|
added_mbody = f"\nAdded {label.lower()}:\n\n"
|
|
1877
|
+
web_base = github_web_base()
|
|
1718
1878
|
for item in added_items:
|
|
1719
|
-
item_url = (f"
|
|
1720
|
-
else f"
|
|
1879
|
+
item_url = (f"{web_base}/{item}/" if label.lower() in ["followers", "followings", "starred repos"]
|
|
1880
|
+
else f"{web_base}/{user}/{item}/")
|
|
1721
1881
|
print(f"- {item} [ {item_url} ]")
|
|
1722
1882
|
added_list_str += f"- {item} [ {item_url} ]\n"
|
|
1723
1883
|
try:
|
|
@@ -1789,7 +1949,7 @@ def check_repo_list_changes(count_old, count_new, list_old, list_new, label, rep
|
|
|
1789
1949
|
print(f"{removal_text} {label.lower()}:\n")
|
|
1790
1950
|
removed_mbody = f"\n{removal_text} {label.lower()}:\n\n"
|
|
1791
1951
|
for item in removed_items:
|
|
1792
|
-
item_line = f"- {item} [
|
|
1952
|
+
item_line = f"- {item} [ {github_web_base()}/{item}/ ]" if label.lower() in ["stargazers", "watchers", "forks"] else f"- {item}"
|
|
1793
1953
|
print(item_line)
|
|
1794
1954
|
removed_list_str += item_line + "\n"
|
|
1795
1955
|
try:
|
|
@@ -1804,7 +1964,7 @@ def check_repo_list_changes(count_old, count_new, list_old, list_new, label, rep
|
|
|
1804
1964
|
print(f"Added {label.lower()}:\n")
|
|
1805
1965
|
added_mbody = f"\nAdded {label.lower()}:\n\n"
|
|
1806
1966
|
for item in added_items:
|
|
1807
|
-
item_line = f"- {item} [
|
|
1967
|
+
item_line = f"- {item} [ {github_web_base()}/{item}/ ]" if label.lower() in ["stargazers", "watchers", "forks"] else f"- {item}"
|
|
1808
1968
|
print(item_line)
|
|
1809
1969
|
added_list_str += item_line + "\n"
|
|
1810
1970
|
try:
|
|
@@ -1983,6 +2143,97 @@ def is_profile_public(g: Github, user, new_account_days=30):
|
|
|
1983
2143
|
|
|
1984
2144
|
return False
|
|
1985
2145
|
|
|
2146
|
+
|
|
2147
|
+
# Returns a dict mapping 'YYYY-MM-DD' -> int contribution count for the range
|
|
2148
|
+
def get_daily_contributions(username: str, start: Optional[dt.date] = None, end: Optional[dt.date] = None, token: Optional[str] = None) -> dict:
|
|
2149
|
+
if token is None:
|
|
2150
|
+
raise ValueError("GitHub token is required")
|
|
2151
|
+
|
|
2152
|
+
today = dt.date.today()
|
|
2153
|
+
if start is None:
|
|
2154
|
+
start = today
|
|
2155
|
+
if end is None:
|
|
2156
|
+
end = today
|
|
2157
|
+
|
|
2158
|
+
url = GITHUB_API_URL.rstrip("/") + "/graphql"
|
|
2159
|
+
headers = {"Authorization": f"Bearer {token}"}
|
|
2160
|
+
|
|
2161
|
+
start_iso = dt.datetime.combine(start, dt.time.min).isoformat()
|
|
2162
|
+
to_exclusive = end + dt.timedelta(days=1)
|
|
2163
|
+
end_iso = dt.datetime.combine(to_exclusive, dt.time.min).isoformat()
|
|
2164
|
+
|
|
2165
|
+
query = """
|
|
2166
|
+
query($login: String!, $from: DateTime!, $to: DateTime!) {
|
|
2167
|
+
user(login: $login) {
|
|
2168
|
+
contributionsCollection(from: $from, to: $to) {
|
|
2169
|
+
contributionCalendar {
|
|
2170
|
+
weeks {
|
|
2171
|
+
contributionDays {
|
|
2172
|
+
date
|
|
2173
|
+
contributionCount
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}"""
|
|
2180
|
+
|
|
2181
|
+
variables = {"login": username, "from": start_iso, "to": end_iso}
|
|
2182
|
+
r = requests.post(url, json={"query": query, "variables": variables}, headers=headers, timeout=30)
|
|
2183
|
+
r.raise_for_status()
|
|
2184
|
+
data = r.json()
|
|
2185
|
+
|
|
2186
|
+
days = data["data"]["user"]["contributionsCollection"]["contributionCalendar"]["weeks"]
|
|
2187
|
+
out = {}
|
|
2188
|
+
for w in days:
|
|
2189
|
+
for d in w["contributionDays"]:
|
|
2190
|
+
date = d["date"]
|
|
2191
|
+
if start <= dt.date.fromisoformat(date) <= end:
|
|
2192
|
+
out[date] = d["contributionCount"]
|
|
2193
|
+
return out
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
# Return contribution count for a single day
|
|
2197
|
+
def get_daily_contributions_count(username: str, day: dt.date, token: str) -> int:
|
|
2198
|
+
data = get_daily_contributions(username, day, day, token)
|
|
2199
|
+
return next(iter(data.values()), 0)
|
|
2200
|
+
|
|
2201
|
+
|
|
2202
|
+
# Checks count for today and decides whether to notify based on stored state.
|
|
2203
|
+
def check_daily_contribs(username: str, token: str, state: dict, min_delta: int = 1, fail_threshold: int = 3) -> tuple[bool, int, bool]:
|
|
2204
|
+
day = today_local()
|
|
2205
|
+
|
|
2206
|
+
try:
|
|
2207
|
+
curr = get_daily_contributions_count(username, day, token=token)
|
|
2208
|
+
state["consecutive_failures"] = 0
|
|
2209
|
+
state["last_error"] = None
|
|
2210
|
+
except Exception as e:
|
|
2211
|
+
state["consecutive_failures"] = state.get("consecutive_failures", 0) + 1
|
|
2212
|
+
state["last_error"] = f"{type(e).__name__}: {e}"
|
|
2213
|
+
error_notify = state["consecutive_failures"] >= fail_threshold
|
|
2214
|
+
return False, state.get("count", 0), error_notify
|
|
2215
|
+
|
|
2216
|
+
prev_day = state.get("day")
|
|
2217
|
+
prev_cnt = state.get("count")
|
|
2218
|
+
|
|
2219
|
+
# New day -> reset baseline silently
|
|
2220
|
+
if prev_day != day:
|
|
2221
|
+
state["day"] = day
|
|
2222
|
+
state["count"] = curr
|
|
2223
|
+
state["prev_count"] = curr
|
|
2224
|
+
return False, curr, False # no notify on rollover
|
|
2225
|
+
|
|
2226
|
+
# Same day -> notify if change >= threshold
|
|
2227
|
+
if prev_cnt is not None and abs(curr - prev_cnt) >= min_delta:
|
|
2228
|
+
state["prev_count"] = prev_cnt
|
|
2229
|
+
state["count"] = curr
|
|
2230
|
+
return True, curr, False
|
|
2231
|
+
|
|
2232
|
+
# No change
|
|
2233
|
+
state["count"] = curr
|
|
2234
|
+
return False, curr, False
|
|
2235
|
+
|
|
2236
|
+
|
|
1986
2237
|
# Monitors activity of the specified GitHub user
|
|
1987
2238
|
def github_monitor_user(user, csv_file_name):
|
|
1988
2239
|
|
|
@@ -2002,6 +2253,8 @@ def github_monitor_user(user, csv_file_name):
|
|
|
2002
2253
|
event_date: datetime | None = None
|
|
2003
2254
|
blocked = None
|
|
2004
2255
|
public = False
|
|
2256
|
+
contrib_state = {}
|
|
2257
|
+
contrib_curr = 0
|
|
2005
2258
|
|
|
2006
2259
|
print("Sneaking into GitHub like a ninja ...")
|
|
2007
2260
|
|
|
@@ -2044,6 +2297,14 @@ def github_monitor_user(user, csv_file_name):
|
|
|
2044
2297
|
public = is_profile_public(g, user)
|
|
2045
2298
|
blocked = is_blocked_by(user) if public else None
|
|
2046
2299
|
|
|
2300
|
+
if TRACK_CONTRIB_CHANGES:
|
|
2301
|
+
contrib_curr = get_daily_contributions_count(user, today_local(), token=GITHUB_TOKEN)
|
|
2302
|
+
contrib_state = {
|
|
2303
|
+
"day": today_local(),
|
|
2304
|
+
"count": contrib_curr,
|
|
2305
|
+
"prev_count": contrib_curr
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2047
2308
|
if not DO_NOT_MONITOR_GITHUB_EVENTS:
|
|
2048
2309
|
events = list(islice(g_user.get_events(), EVENTS_NUMBER))
|
|
2049
2310
|
available_events = len(events)
|
|
@@ -2124,6 +2385,8 @@ def github_monitor_user(user, csv_file_name):
|
|
|
2124
2385
|
print(f"Followings:\t\t\t{followings_count}")
|
|
2125
2386
|
print(f"Repositories:\t\t\t{repos_count}")
|
|
2126
2387
|
print(f"Starred repos:\t\t\t{starred_count}")
|
|
2388
|
+
if TRACK_CONTRIB_CHANGES:
|
|
2389
|
+
print(f"Today's contributions:\t\t{contrib_curr}")
|
|
2127
2390
|
|
|
2128
2391
|
if not DO_NOT_MONITOR_GITHUB_EVENTS:
|
|
2129
2392
|
print(f"Available events:\t\t{available_events}{'+' if available_events == EVENTS_NUMBER else ''}")
|
|
@@ -2242,6 +2505,36 @@ def github_monitor_user(user, csv_file_name):
|
|
|
2242
2505
|
starred_count = starred_raw.totalCount
|
|
2243
2506
|
starred_old, starred_old_count = handle_profile_change("Starred Repos", starred_old_count, starred_count, starred_old, starred_list, user, csv_file_name, field="full_name")
|
|
2244
2507
|
|
|
2508
|
+
# Changed contributions in a day
|
|
2509
|
+
if TRACK_CONTRIB_CHANGES:
|
|
2510
|
+
contrib_notify, contrib_curr, contrib_error_notify = check_daily_contribs(user, GITHUB_TOKEN, contrib_state, min_delta=1, fail_threshold=3)
|
|
2511
|
+
if contrib_error_notify and ERROR_NOTIFICATION:
|
|
2512
|
+
failures = contrib_state.get("consecutive_failures", 0)
|
|
2513
|
+
last_err = contrib_state.get("last_error", "Unknown error")
|
|
2514
|
+
err_msg = f"Error: GitHub daily contributions check failed {failures} times. Last error: {last_err}\n"
|
|
2515
|
+
print(err_msg)
|
|
2516
|
+
send_email(f"GitHub monitor errors for {user}", err_msg + get_cur_ts(nl_ch + "Timestamp: "), "", SMTP_SSL)
|
|
2517
|
+
|
|
2518
|
+
if contrib_notify:
|
|
2519
|
+
contrib_old = contrib_state.get("prev_count")
|
|
2520
|
+
print(f"* Daily contributions changed for user {user} on {get_short_date_from_ts(contrib_state['day'], show_hour=False)} from {contrib_old} to {contrib_curr}!\n")
|
|
2521
|
+
|
|
2522
|
+
try:
|
|
2523
|
+
if csv_file_name:
|
|
2524
|
+
write_csv_entry(csv_file_name, now_local_naive(), "Daily Contribs", user, contrib_old, contrib_curr)
|
|
2525
|
+
except Exception as e:
|
|
2526
|
+
print(f"* Error: {e}")
|
|
2527
|
+
|
|
2528
|
+
m_subject = f"GitHub user {user} daily contributions changed from {contrib_old} to {contrib_curr}!"
|
|
2529
|
+
m_body = (f"GitHub user {user} daily contributions changed on {get_short_date_from_ts(contrib_state['day'], show_hour=False)} from {contrib_old} to {contrib_curr}\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: ')}")
|
|
2530
|
+
|
|
2531
|
+
if CONTRIB_NOTIFICATION:
|
|
2532
|
+
print(f"Sending email notification to {RECEIVER_EMAIL}")
|
|
2533
|
+
send_email(m_subject, m_body, "", SMTP_SSL)
|
|
2534
|
+
|
|
2535
|
+
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)})")
|
|
2536
|
+
print_cur_ts("Timestamp:\t\t\t")
|
|
2537
|
+
|
|
2245
2538
|
# Changed bio
|
|
2246
2539
|
bio = gh_call(lambda: g_user.bio)()
|
|
2247
2540
|
if bio is not None and bio != bio_old:
|
|
@@ -2652,7 +2945,7 @@ def github_monitor_user(user, csv_file_name):
|
|
|
2652
2945
|
|
|
2653
2946
|
|
|
2654
2947
|
def main():
|
|
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
|
|
2948
|
+
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, CONTRIB_NOTIFICATION, TRACK_CONTRIB_CHANGES
|
|
2656
2949
|
|
|
2657
2950
|
if "--generate-config" in sys.argv:
|
|
2658
2951
|
print(CONFIG_BLOCK.strip("\n"))
|
|
@@ -2759,6 +3052,13 @@ def main():
|
|
|
2759
3052
|
default=None,
|
|
2760
3053
|
help="Email when user's repositories update date changes"
|
|
2761
3054
|
)
|
|
3055
|
+
notify.add_argument(
|
|
3056
|
+
"-y", "--notify-daily-contribs",
|
|
3057
|
+
dest="notify_daily_contribs",
|
|
3058
|
+
action="store_true",
|
|
3059
|
+
default=None,
|
|
3060
|
+
help="Email when user's daily contributions count changes"
|
|
3061
|
+
)
|
|
2762
3062
|
notify.add_argument(
|
|
2763
3063
|
"-e", "--no-error-notify",
|
|
2764
3064
|
dest="notify_errors",
|
|
@@ -2858,6 +3158,13 @@ def main():
|
|
|
2858
3158
|
default=None,
|
|
2859
3159
|
help="Disable logging to github_monitor_<username>.log"
|
|
2860
3160
|
)
|
|
3161
|
+
opts.add_argument(
|
|
3162
|
+
"-m", "--track-contribs-changes",
|
|
3163
|
+
dest="track_contribs_changes",
|
|
3164
|
+
action="store_true",
|
|
3165
|
+
default=None,
|
|
3166
|
+
help="Track user's daily contributions count and log changes"
|
|
3167
|
+
)
|
|
2861
3168
|
|
|
2862
3169
|
args = parser.parse_args()
|
|
2863
3170
|
|
|
@@ -3047,12 +3354,18 @@ def main():
|
|
|
3047
3354
|
if args.notify_repo_update_date is True:
|
|
3048
3355
|
REPO_UPDATE_DATE_NOTIFICATION = True
|
|
3049
3356
|
|
|
3357
|
+
if args.notify_daily_contribs is True:
|
|
3358
|
+
CONTRIB_NOTIFICATION = True
|
|
3359
|
+
|
|
3050
3360
|
if args.notify_errors is False:
|
|
3051
3361
|
ERROR_NOTIFICATION = False
|
|
3052
3362
|
|
|
3053
3363
|
if args.track_repos_changes is True:
|
|
3054
3364
|
TRACK_REPOS_CHANGES = True
|
|
3055
3365
|
|
|
3366
|
+
if args.track_contribs_changes is True:
|
|
3367
|
+
TRACK_CONTRIB_CHANGES = True
|
|
3368
|
+
|
|
3056
3369
|
if args.no_monitor_events is True:
|
|
3057
3370
|
DO_NOT_MONITOR_GITHUB_EVENTS = True
|
|
3058
3371
|
|
|
@@ -3060,6 +3373,9 @@ def main():
|
|
|
3060
3373
|
REPO_NOTIFICATION = False
|
|
3061
3374
|
REPO_UPDATE_DATE_NOTIFICATION = False
|
|
3062
3375
|
|
|
3376
|
+
if not TRACK_CONTRIB_CHANGES:
|
|
3377
|
+
CONTRIB_NOTIFICATION = False
|
|
3378
|
+
|
|
3063
3379
|
if DO_NOT_MONITOR_GITHUB_EVENTS:
|
|
3064
3380
|
EVENT_NOTIFICATION = False
|
|
3065
3381
|
|
|
@@ -3068,12 +3384,14 @@ def main():
|
|
|
3068
3384
|
PROFILE_NOTIFICATION = False
|
|
3069
3385
|
REPO_NOTIFICATION = False
|
|
3070
3386
|
REPO_UPDATE_DATE_NOTIFICATION = False
|
|
3387
|
+
CONTRIB_NOTIFICATION = False
|
|
3071
3388
|
ERROR_NOTIFICATION = False
|
|
3072
3389
|
|
|
3073
3390
|
print(f"* GitHub polling interval:\t[ {display_time(GITHUB_CHECK_INTERVAL)} ]")
|
|
3074
|
-
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}]")
|
|
3391
|
+
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[contrib changes = {CONTRIB_NOTIFICATION}] [errors = {ERROR_NOTIFICATION}]")
|
|
3075
3392
|
print(f"* GitHub API URL:\t\t{GITHUB_API_URL}")
|
|
3076
3393
|
print(f"* Track repos changes:\t\t{TRACK_REPOS_CHANGES}")
|
|
3394
|
+
print(f"* Track contrib changes:\t{TRACK_CONTRIB_CHANGES}")
|
|
3077
3395
|
print(f"* Monitor GitHub events:\t{not DO_NOT_MONITOR_GITHUB_EVENTS}")
|
|
3078
3396
|
print(f"* Get owned repos only:\t\t{not GET_ALL_REPOS}")
|
|
3079
3397
|
print(f"* Liveness check:\t\t{bool(LIVENESS_CHECK_INTERVAL)}" + (f" ({display_time(LIVENESS_CHECK_INTERVAL)})" if LIVENESS_CHECK_INTERVAL else ""))
|
|
@@ -1,7 +0,0 @@
|
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|