-
Notifications
You must be signed in to change notification settings - Fork 0
/
dojobot.py
196 lines (150 loc) · 4.81 KB
/
dojobot.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# coding: utf-8
import os
import dbm
from itertools import cycle
import asyncio
from pathlib import Path
import json
import discord
import github
def set_num_teams(n):
global NUM_TEAMS, TEAMS, next_team
NUM_TEAMS = n
TEAMS = range(1, NUM_TEAMS + 1)
next_team = cycle(TEAMS)
set_num_teams(3)
GITHUB_TOKEN = os.environ['GITHUB_TOKEN']
DISCORD_SECRET = os.environ['DISCORD_SECRET']
ADMIN_ROLE = 704428302420541441
config_file = Path(__file__).with_name('config.json')
config = json.loads(config_file.read_text())
db = dbm.open('teams.db', 'c')
teams = {num: [] for num in TEAMS}
client = discord.Client()
gh = github.Github(GITHUB_TOKEN)
def assign_team(username: str) -> int:
"""Assign a user to a team.
The result is stored in the database so that the user will be assigned
to the same team if they register again.
"""
try:
return int(db[username])
except KeyError:
team = next(next_team)
db[username] = str(team)
return team
@client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
HANDLERS = {}
def command(func):
"""Decorator to register a function as a handler for a command."""
HANDLERS[func.__name__] = func
return func
def invite_to_github_repo(username: str, repo_name: str) -> bool:
"""Invite a user to a GitHub repo."""
repo = gh.get_repo(repo_name, lazy=True)
try:
repo.add_to_collaborators(username, 'push')
except Exception:
import traceback
traceback.print_exc()
return False
return True
async def grant_role(guild, member, role_name) -> bool:
"""Grant a Discord role to a user."""
roles = {r.name: r for r in guild.roles}
if role_name not in roles:
return False
await member.add_roles(
roles[role_name],
reason=f"Assigned to {role_name} by bot"
)
return True
@command
async def register(*args, message):
"""Register for the dojo using your GitHub username."""
if len(args) != 2 or args[0] != 'github':
await reply("You should type `!register github <account>`")
author = message.author
channel = message.channel
guild = message.guild
account = args[1]
team = assign_team(str(author))
prefix = config['repo_prefix']
repo = f'{prefix}-team{team}'
loop = asyncio.get_running_loop()
async with channel.typing():
invited, granted = await asyncio.gather(
loop.run_in_executor(
None,
invite_to_github_repo,
account,
repo
),
grant_role(guild, author, f'Team {team}')
)
msgs = [f"<@{author.id}>: You are on team {team}."]
if invited:
msgs.append(f"I have invited you to https://github.com/{repo}.")
else:
msgs.append(f"Something went wrong when adding you to the repo.")
if granted:
msgs.append(f"You now have access to the team {team} channels.")
await channel.send('\n\n'.join(msgs))
@command
async def roles(*args, message):
"""Print the roles you are a member of."""
author = message.author
roles = author.roles
await message.channel.send(
f"<@{author.id}> Your roles: " + ' '.join(f"{role.name}'" for role in roles)
)
def is_herder(member):
"""Return True if the user is a cat herder."""
return any(r.id == ADMIN_ROLE for r in member.roles)
@command
async def teams(*args, message):
"""Set the number of teams."""
author = message.author
if not is_herder(author):
await message.channel.send(f"<@{author.id}> You are not a cat herder.")
return
try:
num, = map(int, args)
except Exception:
await message.channel.send("Usage: !teams <num>")
set_num_teams(num)
for k in list(db.keys()):
del db[k]
await message.channel.send(f"Set to {num} teams.")
@command
async def help(*args, message):
"""Show this help."""
help_text = '\n'.join(
f'{k:<10} {v.__doc__}'
for k, v in HANDLERS.items()
)
await message.channel.send(help_text)
@client.event
async def on_message(message):
if message.author == client.user:
return
if not message.content.startswith('!'):
return
command, *args = message.content.split()
try:
handler = HANDLERS[command.lstrip('!')]
except KeyError:
await message.channel.send(f"I don't know the command {command}")
else:
try:
await handler(
*args,
message=message,
)
except Exception:
import traceback
traceback.print_exc()
await message.channel.send(f"```{traceback.format_exc()}\n```")
client.run(DISCORD_SECRET)