|
1 | 1 | import requests |
2 | 2 |
|
3 | | -# class User(): |
4 | | -# def __init__(self, data: dict): |
5 | | -# self.id = int(data["id"]) |
6 | | -# self.username = data["username"] |
7 | | -# self.avatar = data.get("avatar") |
8 | | -# self.discriminator = data["discriminator"] |
9 | | -# self.public_flags = data.get("public_flags") |
10 | | -# self.flags = data.get("flags") |
11 | | -# self.banner = data.get("banner") |
12 | | -# self.accent_color = data.get("accent_color") |
13 | | -# self.locale = data.get("locale") |
14 | | -# self.mfa_enabled = data.get("mfa_enabled") |
15 | | -# self.nitro_type = data.get("premium_type") |
16 | | -# self.email = data.get("email") |
17 | | -# self.email_verified = data.get("verified") |
18 | | - |
19 | | -# @property |
20 | | -# def avatar_url(self): |
21 | | -# if not self.avatar: return None |
22 | | -# return f"https://cdn.discordapp.com/avatars/{self.id}/{self.avatar}.{'png' if not self.avatar.startswith('a_') else '.gif'}?size=1024" |
23 | | - |
24 | | -# @property |
25 | | -# def banner_url(self): |
26 | | -# if not self.banner: return None |
27 | | -# return f"https://cdn.discordapp.com/banners/{self.id}/{self.banner}.{'png' if not self.avatar.startswith('a_') else '.gif'}?size=1024" |
28 | | - |
29 | | -class access_token(): |
30 | | - def __init__(self, response, client): |
31 | | - self.client = client |
32 | | - self.expires = response["expires_in"] |
33 | | - self.token = response["access_token"] |
34 | | - self.scope = response["scope"].split(" ") |
35 | | - self.refresh_token = response["refresh_token"] |
| 3 | +class PartialAccessToken(): |
| 4 | + def __init__(self, access_token, client) -> None: |
| 5 | + self.client: Client = client |
| 6 | + self.token = access_token |
36 | 7 |
|
37 | | - self.webhook = response.get("webhook") |
38 | | - self.guild = response.get("guild") |
| 8 | + def fetch_identify(self): |
| 9 | + response = requests.get("https://discord.com/api/v10/users/@me", headers={ |
| 10 | + "authorization": f"Bearer {self.token}" |
| 11 | + }) |
39 | 12 |
|
40 | | - self.__identify_cache = None |
41 | | - |
42 | | - def identify(self): |
43 | | - if not "identify" in self.scope: raise exceptions.MissingScope(f"identify scope wasn't granted") |
44 | | - response = requests.get(url="https://discord.com/api/v10/users/@me",headers={ |
45 | | - 'Authorization': f'Bearer {self.token}' |
46 | | - }) |
| 13 | + if response.ok: |
| 14 | + return response.json() |
| 15 | + elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.") |
| 16 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 17 | + else: |
| 18 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
| 19 | + |
| 20 | + def fetch_connections(self): |
| 21 | + response = requests.get("https://discord.com/api/v10/users/@me/connections", headers={ |
| 22 | + "authorization": f"Bearer {self.token}" |
| 23 | + }) |
47 | 24 |
|
48 | | - if response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}") |
49 | | - elif not response.ok: raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
50 | | - user = response.json() |
51 | | - self.__identify_cache = user |
52 | | - return user |
| 25 | + if response.ok: |
| 26 | + return response.json() |
| 27 | + elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.") |
| 28 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 29 | + else: |
| 30 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
| 31 | + |
| 32 | + def fetch_guilds(self): |
| 33 | + response = requests.get("https://discord.com/api/v10/users/@me/guilds", headers={ |
| 34 | + "authorization": f"Bearer {self.token}" |
| 35 | + }) |
53 | 36 |
|
54 | | - def connections(self): |
55 | | - if not "connections" in self.scope: raise exceptions.MissingScope(f"connections scope wasn't granted") |
56 | | - response = requests.get(url="https://discord.com/api/v10/users/@me/connections", headers={ |
57 | | - 'Authorization': f'Bearer {self.token}' |
58 | | - }) |
| 37 | + if response.ok: |
| 38 | + return response.json() |
| 39 | + elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.") |
| 40 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 41 | + else: |
| 42 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
| 43 | + |
| 44 | + def fetch_guild_member(self, guild_id): |
| 45 | + response = requests.get(f"https://discord.com/api/v10/users/@me/guilds/{guild_id}/member", headers={ |
| 46 | + "authorization": f"Bearer {self.token}" |
| 47 | + }) |
59 | 48 |
|
60 | | - if response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}") |
61 | | - elif not response.ok: raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
62 | | - return response.json() |
| 49 | + if response.ok: |
| 50 | + return response.json() |
| 51 | + elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.") |
| 52 | + elif response.status_code == 404: raise exceptions.HTTPException(f"user is not in this guild.") |
| 53 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 54 | + else: |
| 55 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
| 56 | + |
| 57 | + def join_guild(self, guild_id, nick = None, role_ids = None, mute = False, deaf = False): |
| 58 | + response = requests.put(f"https://discord.com/api/v10/guilds/{guild_id}/members/621878678405775379", headers={ |
| 59 | + "authorization": f"Bot {self.client._Client__bot_token}" |
| 60 | + }, json={ |
| 61 | + "access_token": self.token, |
| 62 | + "nick": nick, |
| 63 | + "roles": role_ids, |
| 64 | + "mute": mute, |
| 65 | + "deaf": deaf, |
| 66 | + }) |
63 | 67 |
|
64 | | - def guilds(self): |
65 | | - if not "guilds" in self.scope: raise exceptions.MissingScope(f"guilds scope wasn't granted") |
66 | | - response = requests.get(url="https://discord.com/api/v10/users/@me/guilds",headers={ |
67 | | - 'Authorization': f'Bearer {self.token}' |
68 | | - }) |
69 | | - if response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}") |
70 | | - elif not response.ok: raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
71 | | - return response.json() |
| 68 | + if response.status_code == 204: |
| 69 | + raise exceptions.HTTPException(f"member is already in the guild.") |
| 70 | + elif response.ok: |
| 71 | + return response.json() |
| 72 | + elif response.status_code == 401: raise exceptions.Forbidden(f"this AccessToken does not have the nessasary scope.") |
| 73 | + elif response.status_code == 403: raise exceptions.Forbidden(f"the Bot token must be for a bot in the guild that has permissions to create invites in the target guild and must have any other required permissions.") |
| 74 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 75 | + else: |
| 76 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
72 | 77 |
|
73 | | - def guilds_member(self, guild): |
74 | | - if not "guilds.members.read" in self.scope: raise exceptions.MissingScope(f"guilds.members.read scope wasn't granted") |
75 | | - response = requests.get(url=f"https://discord.com/api/v10/users/@me/guilds/{guild}/member",headers={ |
76 | | - 'Authorization': f'Bearer {self.token}' |
77 | | - }) |
78 | | - if response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}") |
79 | | - elif not response.ok: raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
80 | | - return response.json() |
81 | | - |
82 | | - def guilds_join(self, guild): |
83 | | - if not self.__identify_cache: raise exceptions.BaseException(f"you must call identify before guilds.join!") |
84 | | - if not "guilds.join" in self.scope: raise exceptions.MissingScope(f"guilds.join scope wasn't granted") |
85 | | - response = requests.put(url=f"https://discord.com/api/v10/guilds/{guild}/members/{self.__identify_cache['id']}", json={ |
86 | | - 'Authorization': f'Bot {self.client.bot_token}', |
87 | | - }, headers={ |
88 | | - 'access_token': self.token |
89 | | - }) |
| 78 | +class AccessToken(PartialAccessToken): |
| 79 | + def __init__(self, data: dict, client) -> None: |
| 80 | + super().__init__(data["access_token"], client) |
90 | 81 |
|
91 | | - if response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}") |
92 | | - elif not response.ok: raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
93 | | - return response.json() |
| 82 | + self.expires = data.get("expires_in") |
| 83 | + self.scope = data.get("scope", "").split(" ") |
| 84 | + self.refresh_token = data.get("refresh_token") |
| 85 | + self.webhook = data.get("webhook") |
| 86 | + self.guild = data.get("guild") |
94 | 87 |
|
95 | 88 | class Client(): |
96 | | - def __init__(self, id, secret, redirect, bot_token=None): |
97 | | - self.id = id |
98 | | - self.secret = secret |
99 | | - self.redirect = redirect |
100 | | - self.bot_token = bot_token |
101 | | - |
102 | | - def exchange_code(self, token): |
103 | | - response = requests.post("https://discord.com/api/v10/oauth2/token", data={ |
104 | | - 'client_id': self.id, |
105 | | - 'client_secret': self.secret, |
106 | | - 'grant_type': 'authorization_code', |
107 | | - 'code': token, |
108 | | - 'redirect_uri': self.redirect |
109 | | - }) |
| 89 | + def __init__(self, id, secret, redirect, bot_token=None): |
| 90 | + self.id = id |
| 91 | + self.redirect_url = redirect |
| 92 | + self.__secret = secret |
| 93 | + self.__bot_token = bot_token |
| 94 | + |
| 95 | + def from_access_token(self, access_token): |
| 96 | + return PartialAccessToken(access_token, self) |
| 97 | + |
| 98 | + def exchange_code(self, code): |
| 99 | + response = requests.post("https://discord.com/api/v10/oauth2/token", data={ |
| 100 | + "grant_type": "authorization_code", "code": code, |
| 101 | + "client_id": self.id, "client_secret": self.__secret, |
| 102 | + "redirect_uri": self.redirect_url}) |
110 | 103 |
|
111 | | - if response.status_code == 429: raise Exception(f"You are being Rate Limited") |
112 | | - elif response.status_code != 200: raise Exception(f"Something went wrong. Status Code: {response.status_code}") |
113 | | - return access_token(response.json(), self) |
114 | | - |
115 | | - def refresh_token(self, refresh_token): |
116 | | - response = requests.post("https://discord.com/api/v10/oauth2/token", data={ |
117 | | - 'client_id': self.id, |
118 | | - 'client_secret': self.secret, |
119 | | - 'grant_type': 'refresh_token', |
120 | | - 'refresh_token': refresh_token |
121 | | - }) |
| 104 | + if response.ok: |
| 105 | + return AccessToken(response.json(), self) |
| 106 | + elif response.status_code == 400: raise exceptions.HTTPException("the code, client id, client secret or the redirect uri is invalid/don't match.") |
| 107 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 108 | + else: |
| 109 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
| 110 | + |
| 111 | + def refresh_token(self, refresh_token): |
| 112 | + response = requests.post("https://discord.com/api/v10/oauth2/token", data={ |
| 113 | + "grant_type": "refresh_token", "refresh_token": refresh_token, |
| 114 | + "client_id": self.id, "client_secret": self.__secret}) |
| 115 | + |
| 116 | + if response.ok: |
| 117 | + return AccessToken(response.json(), self) |
| 118 | + elif response.status_code == 400: raise exceptions.HTTPException("the refresh token, client id or client secret is invalid/don't match.") |
| 119 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 120 | + else: |
| 121 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
122 | 122 |
|
123 | | - if response.status_code == 429: raise Exception(f"You are being Rate Limited") |
124 | | - elif response.status_code != 200: raise Exception(f"Something went wrong. Status Code: {response.status_code}") |
125 | | - return access_token(response.json(), self) |
126 | | - |
| 123 | + def client_credentails_grant(self, scope): |
| 124 | + response = requests.post("https://discord.com/api/v10/oauth2/token", data={ |
| 125 | + "grant_type": "client_credentials", "scope": " ".join(scope)}, |
| 126 | + auth=(self.id, self.__secret)) |
| 127 | + |
| 128 | + if response.ok: |
| 129 | + return AccessToken(response.json(), self) |
| 130 | + elif response.status_code == 400: raise exceptions.HTTPException("the scope, client id or client secret is invalid/don't match.") |
| 131 | + elif response.status_code == 429: raise exceptions.RateLimited(f"You are being Rate Limited. Retry after: {response.json()['retry_after']}", retry_after=response.json()['retry_after']) |
| 132 | + else: |
| 133 | + raise exceptions.HTTPException(f"Unexpected HTTP {response.status_code}") |
| 134 | + |
127 | 135 | class exceptions(): |
128 | | - class BaseException(Exception): |
129 | | - pass |
| 136 | + class BaseException(Exception): |
| 137 | + pass |
130 | 138 |
|
131 | | - class HTTPException(BaseException): |
132 | | - pass |
| 139 | + class HTTPException(BaseException): |
| 140 | + pass |
133 | 141 |
|
134 | | - class RateLimited(HTTPException): |
135 | | - def __init__(self, text, retry_after): |
136 | | - self.retry_after = retry_after |
137 | | - super().__init__(text) |
| 142 | + class RateLimited(HTTPException): |
| 143 | + def __init__(self, text, retry_after): |
| 144 | + self.retry_after = retry_after |
| 145 | + super().__init__(text) |
138 | 146 |
|
139 | | - class Forbidden(HTTPException): |
140 | | - pass |
141 | | - |
142 | | - class MissingScope(BaseException): |
143 | | - pass |
| 147 | + class Forbidden(HTTPException): |
| 148 | + pass |
0 commit comments