Skip to content

Commit a395bf0

Browse files
authored
Merge pull request #71 from kyb3r/oauth
Add oauth capabilities
2 parents fb35700 + 73e4a74 commit a395bf0

File tree

5 files changed

+167
-72
lines changed

5 files changed

+167
-72
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,6 @@ ENV/
100100
# mypy
101101
.mypy_cache/
102102
*.json
103+
104+
105+
config.json

bot.py

Lines changed: 102 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
import aiohttp
4545

4646
from utils.paginator import PaginatorSession
47-
from utils.github import Github
48-
47+
from utils.api import Github, ModmailApiClient
4948

5049

5150
class Modmail(commands.Bot):
@@ -237,7 +236,7 @@ async def data_loop(self):
237236
"version": __version__
238237
}
239238

240-
resp = await self.session.post('https://api.kybr.tk/modmail', json=data)
239+
await self.session.post('https://api.kybr.tk/modmail', json=data)
241240

242241
await asyncio.sleep(3600)
243242

@@ -250,13 +249,11 @@ async def get_latest_updates(self, limit=3):
250249
html_url = commit['html_url']
251250
message = commit['commit']['message']
252251
author_name = commit['author']['login']
253-
author_url = commit['author']['html_url']
254252

255253
latest_commits += f'[`{short_sha}`]({html_url}) {message} - {author_name}\n'
256254

257255
return latest_commits
258256

259-
260257
@property
261258
def uptime(self):
262259
now = datetime.datetime.utcnow()
@@ -294,8 +291,7 @@ async def about(self, ctx):
294291
em.description = 'This is an open source discord bot made by kyb3r and '\
295292
'improved upon suggestions by the users! This bot serves as a means for members to '\
296293
'easily communicate with server leadership in an organised manner.'
297-
298-
294+
299295
try:
300296
async with self.session.get('https://api.kybr.tk/modmail') as resp:
301297
meta = await resp.json()
@@ -328,62 +324,126 @@ async def about(self, ctx):
328324

329325
await ctx.send(embed=em)
330326

327+
@commands.group(invoke_without_subcommand=True)
328+
async def github(self, ctx):
329+
if ctx.invoked_subcommand:
330+
return
331+
332+
client = ModmailApiClient(self)
333+
data = await client.get_user_info()
334+
335+
prefix = self.config.get('PREFIX', 'm.')
336+
337+
em = discord.Embed(
338+
title='Github',
339+
color=discord.Color.red(),
340+
description=f'Not logged in, do `{prefix}github login` to login with GitHub.'
341+
)
342+
em.add_field(name='Subcommands', value=f'`{prefix}github login`\n`{prefix}github logout`')
343+
344+
if not data['error']:
345+
user = data['user']
346+
em.color = discord.Color.green()
347+
em.description = f"Currently logged in."
348+
em.set_author(name=user['username'], icon_url=user['avatar_url'], url=user['url'])
349+
em.set_thumbnail(url=user['avatar_url'])
350+
await ctx.send(embed=em)
351+
else:
352+
await ctx.send(embed=em)
353+
354+
@github.command(name='login')
355+
async def _login(self, ctx):
356+
client = ModmailApiClient(self)
357+
358+
oauth_url = 'https://github.com/login/oauth/authorize?client_id' \
359+
'=e54e4ff0f234ee9f22aa&scope=public_repo&redirect_uri=' \
360+
'https://api.kybr.tk/modmail/github/callback' \
361+
f'?token={client.token}'
362+
363+
em = discord.Embed(
364+
color=discord.Color.green(),
365+
title='Login with GitHub',
366+
description='In order to use the update command, you need ' \
367+
'to have fork the [repo](https://github.com/kyb3r/modmail) and ' \
368+
'login with GitHub so that we can update your fork to ' \
369+
'match the main repository whenever there is an update.' \
370+
'Click the link below to be taken to log in with github to authorize Modmail.'
371+
)
372+
em.set_thumbnail(url='https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png')
373+
374+
em.add_field(name='Login', value=f'[Click Here]({oauth_url})', inline=False)
375+
em.add_field(name='Warning', value='Dont share this link as it contains sensitive information.')
376+
await ctx.author.send(embed=em)
377+
378+
@github.command(name='logout')
379+
async def _logout(self, ctx):
380+
client = ModmailApiClient(self)
381+
data = await client.logout()
382+
383+
em = discord.Embed(
384+
color=discord.Color.green(),
385+
title='Logged out',
386+
description='Successfully logged out.'
387+
)
388+
389+
if data['error']:
390+
em.color = discord.Color.red()
391+
em.title = 'Error'
392+
em.description = 'You are not logged in already.'
393+
394+
await ctx.send(embed=em)
331395

332396
@commands.command()
333397
async def update(self, ctx):
334398
'''Updates the bot, this only works with heroku users.'''
335399
allowed = [int(x) for x in self.config.get('OWNERS', '').split(',')]
336400

337-
if ctx.author.id not in allowed:
401+
if ctx.author.id not in allowed:
338402
return
339403

340-
async with self.session.get('https://api.kybr.tk/modmail') as resp:
341-
data = await resp.json()
342-
404+
client = ModmailApiClient(self)
405+
406+
metadata = await client.get_metadata()
407+
343408
em = discord.Embed(
344409
title='Already up to date',
345410
description=f'The latest version is [`{__version__}`](https://github.com/kyb3r/modmail/blob/master/bot.py#L25)',
346411
color=discord.Color.green()
347-
)
348-
349-
access_token = self.config.get('GITHUB_ACCESS_TOKEN')
350-
351-
if data['latest_version'] == __version__:
352-
if access_token:
353-
user = await Github.login(self, access_token)
354-
em.set_author(name=user.username, icon_url=user.avatar_url, url=user.url)
355-
356-
if data['latest_version'] != __version__:
357-
if not access_token:
358-
em.title = 'Invalid Access Token'
359-
em.description = 'You have not properly set up GitHub credentials. '\
360-
'Create a config variable named `GITHUB_ACCESS_TOKEN`'\
361-
' and set the value as your personal access token which'\
362-
' can be generated in your GitHub account\'s [developer '\
363-
'settings](https://github.com/settings/tokens).'
412+
)
413+
414+
if metadata['latest_version'] == __version__:
415+
data = await client.get_user_info()
416+
if not data['error']:
417+
user = data['user']
418+
em.set_author(name=user['username'], icon_url=user['avatar_url'], url=user['url'])
364419

420+
if metadata['latest_version'] != __version__:
421+
data = await client.update_repository()
422+
423+
if data['error']:
424+
prefix = self.config.get('PREFIX', 'm.')
425+
em.title = 'Unauthorised'
426+
em.description = f"You haven't logged in with github yet. Type the command `{prefix}github login` to authorize this bot."
365427
em.color = discord.Color.red()
366428
return await ctx.send(embed=em)
367-
368-
em.set_footer(text=f"Updating modmail v{__version__} -> v{data['latest_version']}")
369-
370-
user = await Github.login(self, access_token)
371-
data = await user.update_repository()
372429

430+
commit_data = data['data']
431+
user = data['user']
373432
em.title = 'Success'
374-
em.set_author(name=user.username, icon_url=user.avatar_url, url=user.url)
375-
376-
if data:
433+
em.set_author(name=user['username'], icon_url=user['avatar_url'], url=user['url'])
434+
em.set_footer(text=f"Updating modmail v{__version__} -> v{metadata['latest_version']}")
435+
436+
if commit_data:
377437
em.description = 'Bot successfully updated, the bot will restart momentarily'
378-
message = data['commit']['message']
379-
html_url = data["html_url"]
380-
short_sha = data['sha'][:6]
381-
em.add_field(name='Merge Commit', value=f'[`{short_sha}`]({html_url}) {message} - {user.username}')
438+
message = commit_data['commit']['message']
439+
html_url = commit_data["html_url"]
440+
short_sha = commit_data['sha'][:6]
441+
em.add_field(name='Merge Commit', value=f"[`{short_sha}`]({html_url}) {message} - {user['username']}")
382442
else:
383443
em.description = 'Already up to date with master repository.'
384-
444+
385445
em.add_field(name='Latest Commit', value=await self.get_latest_updates(limit=1), inline=False)
386-
446+
387447
await ctx.send(embed=em)
388448

389449
@commands.command()

confg.json.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"TOKEN": "XXX.XXX.XXX",
3+
"PREFIX": "m.",
4+
"GUILD_ID": EDITTHIS,
5+
"STATUS": "DM me for help!",
6+
"OWNERS": "comma,seperated,user_ids"
7+
}

config.json

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,47 @@
1-
class Github:
1+
from hashlib import sha256
2+
3+
class ApiClient:
4+
def __init__(self, app):
5+
self.app = app
6+
self.session = app.session
7+
self.headers = None
8+
9+
async def request(self, url, method='GET', payload=None):
10+
async with self.session.request(method, url, headers=self.headers, json=payload) as resp:
11+
try:
12+
return await resp.json()
13+
except:
14+
return await resp.text()
15+
16+
17+
class Github(ApiClient):
218
head = 'https://api.github.com/repos/kyb3r/modmail/git/refs/heads/master'
319
merge_url = 'https://api.github.com/repos/{username}/modmail/merges'
420
commit_url = 'https://api.github.com/repos/kyb3r/modmail/commits'
521

6-
def __init__(self, bot, access_token=None, username=None):
7-
self.bot = bot
8-
self.session = bot.session
9-
self.access_token = access_token
10-
self.username = username
22+
def __init__(self, app, access_token=None):
23+
super().__init__(app)
24+
self.username = None
1125
self.avatar_url = None
1226
self.url = None
1327
self.headers = None
14-
if self.access_token:
15-
self.headers = {'Authorization': 'Bearer '+ str(access_token)}
16-
28+
if access_token:
29+
self.headers = {'Authorization': 'token ' + str(access_token)}
30+
31+
@classmethod
32+
async def login(cls, bot, access_token):
33+
self = cls(bot, access_token)
34+
resp = await self.request('https://api.github.com/user')
35+
self.username = resp['login']
36+
self.avatar_url = resp['avatar_url']
37+
self.url = resp['html_url']
38+
return self
39+
1740
async def get_latest_commits(self, limit=3):
1841
resp = await self.request(self.commit_url)
1942
for index in range(limit):
2043
yield resp[index]
21-
44+
2245
async def update_repository(self, sha=None):
2346
if sha is None:
2447
resp = await self.request(self.head)
@@ -36,18 +59,26 @@ async def update_repository(self, sha=None):
3659
if isinstance(resp, dict):
3760
return resp
3861

39-
async def request(self, url, method='GET', payload=None):
40-
async with self.session.request(method, url, headers=self.headers, json=payload) as resp:
41-
try:
42-
return await resp.json()
43-
except:
44-
return await resp.text()
62+
class ModmailApiClient(ApiClient):
63+
64+
base = 'https://api.kybr.tk/modmail'
65+
github = base + '/github'
66+
67+
def __init__(self, bot):
68+
super().__init__(bot)
69+
self.token = str(sha256(bot.token.encode()).hexdigest()) # added security
70+
self.headers = {
71+
'Authorization': 'Bearer ' + self.token
72+
}
4573

46-
@classmethod
47-
async def login(cls, bot, access_token):
48-
self = cls(bot, access_token)
49-
resp = await self.request('https://api.github.com/user')
50-
self.username = resp['login']
51-
self.avatar_url = resp['avatar_url']
52-
self.url = resp['html_url']
53-
return self
74+
def get_user_info(self):
75+
return self.request(self.github + '/userinfo')
76+
77+
def update_repository(self):
78+
return self.request(self.github + '/update-repository')
79+
80+
def logout(self):
81+
return self.request(self.github + '/logout')
82+
83+
def get_metadata(self):
84+
return self.request(self.base)

0 commit comments

Comments
 (0)