Skip to content

Commit 21b3b54

Browse files
committed
docs: readme overhaul
1 parent 0cebb35 commit 21b3b54

File tree

3 files changed

+149
-103
lines changed

3 files changed

+149
-103
lines changed

README.md

Lines changed: 136 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,183 @@
1-
# [topgg][pypi-url] [![pypi][pypi-image]][pypi-url] [![downloads][downloads-image]][pypi-url]
1+
# Top.gg Python SDK
22

3-
[pypi-image]: https://img.shields.io/pypi/v/topggpy.svg?style=flat-square
4-
[pypi-url]: https://pypi.org/project/topggpy/
5-
[downloads-image]: https://img.shields.io/pypi/dm/topggpy?style=flat-square
3+
The community-maintained Python library for Top.gg.
64

7-
A simple API wrapper for [Top.gg](https://top.gg) written in Python.
5+
## Installation
86

9-
## Getting started
7+
```sh
8+
$ pip install topggpy
9+
```
1010

11-
Make sure you already have an API token handy. See [this tutorial](https://github.com/top-gg/rust-sdk/assets/60427892/d2df5bd3-bc48-464c-b878-a04121727bff) on how to retrieve it.
11+
## Setting up
1212

13-
After that, run the following command in your terminal:
13+
### Automatic cleanup
1414

15-
```console
16-
$ pip install topggpy
17-
```
15+
```py
16+
import topgg
1817

19-
## Basic examples
18+
import os
2019

21-
For more information, please read the [documentation](https://topggpy.readthedocs.io/en/latest/).
20+
async with topgg.Client(os.getenv('TOPGG_TOKEN')) as client:
21+
# ...
22+
```
23+
24+
### Manual cleanup
2225

2326
```py
24-
# Import the module.
2527
import topgg
2628

27-
import asyncio
2829
import os
2930

31+
client = topgg.Client(os.getenv('TOPGG_TOKEN'))
3032

31-
async def main() -> None:
33+
# ...
3234

33-
# Declare the client.
34-
async with topgg.Client(os.getenv('TOPGG_TOKEN')) as tg:
35-
36-
# Fetch a bot from its ID.
37-
bot = await tg.get_bot(432610292342587392)
35+
await client.close()
36+
```
3837

39-
print(bot)
38+
## Usage
4039

41-
# Fetch bots that matches the specified query.
42-
bots = await tg.get_bots(
43-
limit=250,
44-
offset=50,
45-
sort_by=topgg.SortBy.MONTHLY_VOTES
46-
)
40+
### Getting a bot
4741

48-
for b in bots:
49-
print(b)
42+
```py
43+
bot = await client.get_bot(432610292342587392)
44+
```
5045

51-
# Post your bot's server count to the API. This will update the server count in your bot's Top.gg page.
52-
await tg.post_server_count(2)
46+
### Getting several bots
5347

54-
# Fetch your bot's posted server count.
55-
posted_server_count = await tg.get_server_count()
48+
#### With defaults
5649

57-
# Fetch your bot's last 1000 unique voters.
58-
voters = await tg.get_voters()
50+
```py
51+
bots = await client.get_bots()
5952

60-
for voter in voters:
61-
print(voter)
53+
for bot in bots:
54+
print(bot)
55+
```
6256

63-
# Check if a user has voted your bot.
64-
has_voted = await tg.has_voted(661200758510977084)
57+
#### With explicit arguments
6558

66-
if has_voted:
67-
print('This user has voted!')
59+
```py
60+
bots = await client.get_bots(limit=250, offset=50, sort_by=topgg.SortBy.MONTHLY_VOTES)
6861

69-
# Check if the weekend multiplier is active, where a single vote counts as two.
70-
is_weekend = await tg.is_weekend()
62+
for bot in bots:
63+
print(bot)
64+
```
7165

72-
if is_weekend:
73-
print('The weekend multiplier is active!')
66+
### Getting your bot's voters
7467

68+
#### First page
7569

76-
if __name__ == '__main__':
77-
78-
# See https://stackoverflow.com/questions/45600579/asyncio-event-loop-is-closed-when-getting-loop
79-
# for more details.
80-
if os.name == 'nt':
81-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
82-
83-
asyncio.run(main())
70+
```py
71+
voters = await client.get_voters()
72+
73+
for voter in voters:
74+
print(voter)
8475
```
8576

86-
## Autoposting example
77+
#### Subsequent pages
8778

8879
```py
89-
# Import the module.
90-
import topgg
80+
voters = await client.get_voters(2)
9181

92-
import asyncio
93-
import os
82+
for voter in voters:
83+
print(voter)
84+
```
9485

86+
### Check if a user has voted for your bot
9587

96-
async def main() -> None:
88+
```py
89+
has_voted = await client.has_voted(661200758510977084)
90+
```
9791

98-
# Declare the client.
99-
tg = topgg.Client(os.getenv('TOPGG_TOKEN'))
92+
### Getting your bot's server count
10093

101-
# Callback to retrieve server count data (required).
102-
@tg.autopost_retrieval
103-
def get_server_count() -> int:
104-
return 2
94+
```py
95+
posted_server_count = await client.get_server_count()
96+
```
10597

106-
# Callback upon successful server count autoposting (optional).
107-
@tg.autopost_success
108-
def success(server_count: int) -> None:
109-
print(f'Successfully posted {server_count} servers to the API!')
98+
### Posting your bot's server count
99+
100+
```py
101+
await client.post_server_count(bot.server_count)
102+
```
103+
104+
### Automatically posting your bot's server count every few minutes
105+
106+
```py
107+
@client.autopost_retrieval
108+
def get_server_count() -> int:
109+
return bot.server_count
110110

111-
# Error handler upon HTTP-related posting failure (optional).
112-
@tg.autopost_error
113-
def error(error: topgg.Error) -> None:
114-
print(f'Error: {error!r}')
111+
@client.autopost_success
112+
def success(server_count: int) -> None:
113+
print(f'Successfully posted {server_count} servers to the API!')
115114

116-
# Start the autoposter.
117-
tg.start_autoposter()
115+
@client.autopost_error
116+
def error(error: topgg.Error) -> None:
117+
print(f'Error: {error!r}')
118118

119-
# Your other logic here...
119+
client.start_autoposter()
120120

121-
# Client session cleanup while also implicitly calling tg.stop_autoposter().
122-
await tg.close()
121+
# ...
122+
123+
client.stop_autoposter() # Optional
124+
```
125+
126+
### Checking if the weekend vote multiplier is active
127+
128+
```py
129+
is_weekend = await client.is_weekend()
130+
```
131+
132+
### Generating widget URLs
133+
134+
#### Large
135+
136+
```py
137+
widget_url = topgg.widget.large(topgg.WidgetType.DISCORD_BOT, 574652751745777665)
138+
```
139+
140+
#### Votes
141+
142+
```py
143+
widget_url = topgg.widget.votes(topgg.WidgetType.DISCORD_BOT, 574652751745777665)
144+
```
145+
146+
#### Owner
147+
148+
```py
149+
widget_url = topgg.widget.owner(topgg.WidgetType.DISCORD_BOT, 574652751745777665)
150+
```
151+
152+
#### Social
153+
154+
```py
155+
widget_url = topgg.widget.social(topgg.WidgetType.DISCORD_BOT, 574652751745777665)
156+
```
157+
158+
### Webhooks
159+
160+
#### Being notified whenever someone voted for your bot
161+
162+
```py
163+
import topgg
164+
165+
import asyncio
166+
import os
167+
168+
port = 8080
169+
secret = os.getenv('MY_TOPGG_WEBHOOK_SECRET')
170+
171+
webhooks = topgg.Webhooks(secret, port)
172+
173+
@webhooks.on_vote('/votes')
174+
def voted(vote: topgg.Vote) -> None:
175+
print(f'A user with the ID of {vote.voter_id} has voted us on Top.gg!')
176+
177+
async def main() -> None:
178+
await webhooks.start() # Starts the server
179+
await asyncio.Event().wait() # Keeps the server alive through indefinite blocking
123180

124181
if __name__ == '__main__':
125-
126-
# See https://stackoverflow.com/questions/45600579/asyncio-event-loop-is-closed-when-getting-loop
127-
# for more details.
128-
if os.name == 'nt':
129-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
130-
131182
asyncio.run(main())
132183
```

topgg/client.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def __init__(self, token: str, *, session: Optional[ClientSession] = None):
102102

103103
self.id = int(json.loads(b64decode(encoded_json))['id'])
104104
except (IndexError, ValueError, binascii.Error, json.decoder.JSONDecodeError):
105-
raise ValueError('Got a malformed API token.')
105+
raise ValueError('Got a malformed API token.') from None
106106

107107
endpoint_ratelimits = namedtuple('EndpointRatelimits', 'global_ bot')
108108

@@ -473,8 +473,9 @@ def start_autoposter(self, interval: Optional[float] = None) -> None:
473473
"""
474474

475475
if not self.autoposter_running:
476-
if self.__autopost_retrieval_callback is None:
477-
raise TypeError('Missing autopost_retrieval callback.')
476+
assert (
477+
self.__autopost_retrieval_callback is not None
478+
), 'Missing autopost_retrieval callback.'
478479

479480
self.__autopost_task = asyncio.create_task(self.__autopost_loop(interval))
480481

topgg/webhooks.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,14 @@
3636
class Vote:
3737
"""A dispatched Top.gg vote event."""
3838

39-
__slots__ = ('receiver_id', 'voter_id', 'is_server', 'is_test', 'is_weekend', 'query')
39+
__slots__ = ('receiver_id', 'voter_id', 'is_test', 'is_weekend', 'query')
4040

4141
receiver_id: int
4242
"""The ID of the Discord bot/server that received a vote."""
4343

4444
voter_id: int
4545
"""The ID of the Top.gg user who voted."""
4646

47-
is_server: bool
48-
"""Whether this vote's receiver is a Discord server."""
49-
5047
is_test: bool
5148
"""Whether this vote is just a test done from the page settings."""
5249

@@ -57,9 +54,10 @@ class Vote:
5754
"""Query strings found on the vote page."""
5855

5956
def __init__(self, json: dict):
60-
self.receiver_id = int(json.get('bot', json['guild']))
57+
guild = json.get('guild')
58+
59+
self.receiver_id = int(json.get('bot', guild))
6160
self.voter_id = int(json['user'])
62-
self.is_server = bool(json.get('guild'))
6361
self.is_test = json['type'] == 'test'
6462
self.is_weekend = bool(json.get('isWeekend'))
6563

@@ -124,14 +122,12 @@ def on_vote(
124122
:rtype: Union[:data:`~.webhooks.OnVoteCallback`, :data:`~.webhooks.OnVoteDecorator`]
125123
"""
126124

127-
if not isinstance(route, str):
125+
if not isinstance(route, str) or not route:
128126
raise TypeError('Missing route argument.')
129127

130-
if auth is None:
131-
auth = self.__default_auth
128+
auth = auth or self.__default_auth
132129

133-
if auth is None:
134-
raise TypeError('Missing password.')
130+
assert auth is not None, 'Missing password.'
135131

136132
def decorator(inner_callback: OnVoteCallback) -> RawCallback:
137133
async def handler(request: web.Request) -> web.Response:
@@ -167,11 +163,9 @@ async def start(self, port: Optional[int] = None) -> None:
167163
"""
168164

169165
if not self.running:
170-
if port is None:
171-
port = self.__default_port
166+
port = port or self.__default_port
172167

173-
if port is None:
174-
raise TypeError('Missing port.')
168+
assert port is not None, 'Missing port.'
175169

176170
runner = web.AppRunner(self.__app)
177171
await runner.setup()

0 commit comments

Comments
 (0)