forked from ekmartin/slack-pull-reminder
-
Notifications
You must be signed in to change notification settings - Fork 2
/
slack_pull_reminder.py
151 lines (110 loc) · 4.1 KB
/
slack_pull_reminder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import os
import sys
from collections import defaultdict
from datetime import datetime, timezone, timedelta
import requests
from github3 import login
POST_URL = 'https://slack.com/api/chat.postMessage'
ignore = os.environ.get('IGNORE_WORDS')
IGNORE_WORDS = [i.lower().strip() for i in ignore.split(',')] if ignore else []
repositories = os.environ.get('REPOSITORIES')
REPOSITORIES = [r.lower().strip() for r in repositories.split(',')] if repositories else []
usernames = os.environ.get('USERNAMES')
USERNAMES = [u.lower().strip() for u in usernames.split(',')] if usernames else []
SLACK_CHANNEL = os.environ.get('SLACK_CHANNEL', '#general')
try:
SLACK_API_TOKEN = os.environ['SLACK_API_TOKEN']
GITHUB_API_TOKEN = os.environ['GITHUB_API_TOKEN']
ORGANIZATION = os.environ['ORGANIZATION']
except KeyError as error:
sys.stderr.write('Please set the environment variable {0}'.format(error))
sys.exit(1)
INITIAL_MESSAGE = """\
Hi! There's a few open pull requests you should take a \
look at:
"""
def fetch_repository_pulls(repository):
pulls = []
for short_pull in repository.pull_requests():
pull = repository.pull_request(short_pull.number)
if pull.state == 'open' and (not USERNAMES or pull.user.login.lower() in USERNAMES):
pulls.append(pull)
return pulls
def is_valid_title(title):
lowercase_title = title.lower()
for ignored_word in IGNORE_WORDS:
if ignored_word in lowercase_title:
return False
return True
def get_age(pull):
td = datetime.now(timezone.utc) - pull.updated_at
hours, _ = divmod(int(td.total_seconds()), 3600)
return hours
def get_review_statuses(pull):
dict = defaultdict(set)
for review in pull.reviews():
if review.state == 'APPROVED':
state = ':white_check_mark:'
elif review.state == 'CHANGES_REQUESTED':
state = ':o:'
else:
continue
dict[state].add('@{0}'.format(review.user.login))
if dict:
line = 'Reviews: ' + ' '.join(['{0} by {1}'.format(key, ', '.join(value)) for (key, value) in dict.items()])
else:
line = 'No reviews :warning:'
return line
def is_mergeable(pull):
line = ':-1:'
if pull.mergeable:
line = ':+1:'
return line
def encode(text):
"""
Replace special symbols as per https://api.slack.com/docs/message-formatting.
"""
return text.replace('&', '&').replace('<', '<').replace('>', '>')
def format_pull_requests(pull_requests, owner, repository):
lines = []
for pull in pull_requests:
if is_valid_title(pull.title):
creator = pull.user.login
review_statuses = get_review_statuses(pull)
mergeable = is_mergeable(pull)
age = get_age(pull)
line = '*[{0}/{1}]* <{2}|{3}> by @{4} • Updated {5}h ago • {6} • Mergeable: {7}'.format(
owner, repository, pull.html_url, encode(pull.title), creator, age, review_statuses, mergeable)
lines.append(line)
return lines
def fetch_organization_pulls(organization_name):
"""
Returns a formatted string list of open pull request messages.
"""
client = login(token=GITHUB_API_TOKEN)
organization = client.organization(organization_name)
lines = []
for repository in organization.repositories():
if REPOSITORIES and repository.name.lower() not in REPOSITORIES:
continue
unchecked_pulls = fetch_repository_pulls(repository)
lines += format_pull_requests(unchecked_pulls, organization_name,
repository.name)
return lines
def send_to_slack(text):
payload = {
'token': SLACK_API_TOKEN,
'channel': SLACK_CHANNEL,
'text': text
}
response = requests.post(POST_URL, data=payload)
answer = response.json()
if not answer['ok']:
raise Exception(answer['error'])
def cli():
lines = fetch_organization_pulls(ORGANIZATION)
if lines:
text = INITIAL_MESSAGE + '\n'.join(lines)
send_to_slack(text)
if __name__ == '__main__':
cli()