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
|
import logging
import random
from datetime import datetime
from urllib.parse import quote, quote_plus
import discord
from discord.ext import commands
from bot.bot import Bot
from bot.constants import Colours, NEGATIVE_REPLIES
from bot.exts.core.extensions import invoke_help_command
log = logging.getLogger(__name__)
GITHUB_API_URL = "https://api.github.com"
class GithubInfo(commands.Cog):
"""Fetches info from GitHub."""
def __init__(self, bot: Bot):
self.bot = bot
async def fetch_data(self, url: str) -> dict:
"""Retrieve data as a dictionary."""
async with self.bot.http_session.get(url) as r:
return await r.json()
@commands.group(name="github", aliases=("gh", "git"))
@commands.cooldown(1, 10, commands.BucketType.user)
async def github_group(self, ctx: commands.Context) -> None:
"""Commands for finding information related to GitHub."""
if ctx.invoked_subcommand is None:
await invoke_help_command(ctx)
@github_group.command(name="user", aliases=("userinfo",))
async def github_user_info(self, ctx: commands.Context, username: str) -> None:
"""Fetches a user's GitHub information."""
async with ctx.typing():
user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{quote_plus(username)}")
# User_data will not have a message key if the user exists
if "message" in user_data:
embed = discord.Embed(
title=random.choice(NEGATIVE_REPLIES),
description=f"The profile for `{username}` was not found.",
colour=Colours.soft_red
)
await ctx.send(embed=embed)
return
org_data = await self.fetch_data(user_data["organizations_url"])
orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data]
orgs_to_add = " | ".join(orgs)
gists = user_data["public_gists"]
# Forming blog link
if user_data["blog"].startswith("http"): # Blog link is complete
blog = user_data["blog"]
elif user_data["blog"]: # Blog exists but the link is not complete
blog = f"https://{user_data['blog']}"
else:
blog = "No website link available"
embed = discord.Embed(
title=f"`{user_data['login']}`'s GitHub profile info",
description=f"```\n{user_data['bio']}\n```\n" if user_data["bio"] else "",
colour=discord.Colour.og_blurple(),
url=user_data["html_url"],
timestamp=datetime.strptime(user_data["created_at"], "%Y-%m-%dT%H:%M:%SZ")
)
embed.set_thumbnail(url=user_data["avatar_url"])
embed.set_footer(text="Account created at")
if user_data["type"] == "User":
embed.add_field(
name="Followers",
value=f"[{user_data['followers']}]({user_data['html_url']}?tab=followers)"
)
embed.add_field(
name="Following",
value=f"[{user_data['following']}]({user_data['html_url']}?tab=following)"
)
embed.add_field(
name="Public repos",
value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)"
)
if user_data["type"] == "User":
embed.add_field(
name="Gists",
value=f"[{gists}](https://gist.github.com/{quote_plus(username, safe='')})"
)
embed.add_field(
name=f"Organization{'s' if len(orgs)!=1 else ''}",
value=orgs_to_add if orgs else "No organizations."
)
embed.add_field(name="Website", value=blog)
await ctx.send(embed=embed)
@github_group.command(name='repository', aliases=('repo',))
async def github_repo_info(self, ctx: commands.Context, *repo: str) -> None:
"""
Fetches a repositories' GitHub information.
The repository should look like `user/reponame` or `user reponame`.
"""
repo = "/".join(repo)
if repo.count("/") != 1:
embed = discord.Embed(
title=random.choice(NEGATIVE_REPLIES),
description="The repository should look like `user/reponame` or `user reponame`.",
colour=Colours.soft_red
)
await ctx.send(embed=embed)
return
async with ctx.typing():
repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote(repo)}")
# There won't be a message key if this repo exists
if "message" in repo_data:
embed = discord.Embed(
title=random.choice(NEGATIVE_REPLIES),
description="The requested repository was not found.",
colour=Colours.soft_red
)
await ctx.send(embed=embed)
return
embed = discord.Embed(
title=repo_data["name"],
description=repo_data["description"],
colour=discord.Colour.og_blurple(),
url=repo_data["html_url"]
)
# If it's a fork, then it will have a parent key
try:
parent = repo_data["parent"]
embed.description += f"\n\nForked from [{parent['full_name']}]({parent['html_url']})"
except KeyError:
log.debug("Repository is not a fork.")
repo_owner = repo_data["owner"]
embed.set_author(
name=repo_owner["login"],
url=repo_owner["html_url"],
icon_url=repo_owner["avatar_url"]
)
repo_created_at = datetime.strptime(repo_data["created_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y")
last_pushed = datetime.strptime(repo_data["pushed_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y at %H:%M")
embed.set_footer(
text=(
f"{repo_data['forks_count']} ⑂ "
f"• {repo_data['stargazers_count']} ⭐ "
f"• Created At {repo_created_at} "
f"• Last Commit {last_pushed}"
)
)
await ctx.send(embed=embed)
def setup(bot: Bot) -> None:
"""Load the GithubInfo cog."""
bot.add_cog(GithubInfo(bot))
|