From 188f21235e7b5b4ab89947c441653a57b16ab318 Mon Sep 17 00:00:00 2001 From: dolfies Date: Tue, 22 Mar 2022 22:47:56 -0400 Subject: [PATCH] Improve readme, examples, workflows --- .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/python-publish.yml | 2 +- .github/workflows/test.yml | 2 +- README.rst | 42 ++++---- examples/basic_bot.py | 8 +- examples/basic_voice.py | 8 +- examples/converters.py | 9 +- examples/custom_context.py | 8 +- examples/deleted.py | 7 +- examples/edits.py | 7 +- examples/guessing_game.py | 11 +- examples/new_member.py | 7 +- examples/reaction_roles.py | 7 +- examples/reply.py | 7 +- examples/secret.py | 7 +- examples/views/confirm.py | 62 ------------ examples/views/counter.py | 48 --------- examples/views/dropdown.py | 68 ------------- examples/views/ephemeral.py | 52 ---------- examples/views/link.py | 44 -------- examples/views/persistent.py | 68 ------------- examples/views/tic_tac_toe.py | 144 --------------------------- pyproject.toml | 1 - setup.py | 4 +- 25 files changed, 41 insertions(+), 586 deletions(-) delete mode 100644 examples/views/confirm.py delete mode 100644 examples/views/counter.py delete mode 100644 examples/views/dropdown.py delete mode 100644 examples/views/ephemeral.py delete mode 100644 examples/views/link.py delete mode 100644 examples/views/persistent.py delete mode 100644 examples/views/tic_tac_toe.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b312a113..f05f7d35f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: [ '3.8' ] - name: dists & docs ${{ matrix.python-version }} + name: dists & docs steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index dcb59be29..4761544c9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: [ '3.8' ] - name: check ${{ matrix.python-version }} + name: lint steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 77ab0cb86..c7b32960f 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -11,8 +11,8 @@ on: jobs: deploy: + name: push to PyPi runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up Python diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 00c0e74b7..dbd00a907 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: matrix: python-version: [ '3.8' ] - name: pytest ${{ matrix.python-version }} + name: pytest steps: - uses: actions/checkout@v2 with: diff --git a/README.rst b/README.rst index dd2df58ec..164a19231 100644 --- a/README.rst +++ b/README.rst @@ -1,14 +1,14 @@ -discord.py -========== - -.. image:: https://discord.com/api/guilds/336642139381301249/embed.png - :target: https://discord.gg/r3sSKJJ - :alt: Discord server invite -.. image:: https://img.shields.io/pypi/v/discord.py.svg - :target: https://pypi.python.org/pypi/discord.py +discord.py-self +================ + +.. image:: https://img.shields.io/endpoint?url=https%3A%2F%2Frunkit.io%2Fdamiankrawczyk%2Ftelegram-badge%2Fbranches%2Fmaster%3Furl%3Dhttps%3A%2F%2Ft.me%2Fdpy_self + :target: https://t.me/dpy_self + :alt: Telegram chat +.. image:: https://img.shields.io/pypi/v/discord.py-self.svg + :target: https://pypi.python.org/pypi/discord.py-self :alt: PyPI version info .. image:: https://img.shields.io/pypi/pyversions/discord.py.svg - :target: https://pypi.python.org/pypi/discord.py + :target: https://pypi.python.org/pypi/discord.py-self :alt: PyPI supported Python versions A modern, easy to use, feature-rich, and async ready API wrapper for Discord written in Python. @@ -30,28 +30,28 @@ To install the library without full voice support, you can just run the followin .. code:: sh # Linux/macOS - python3 -m pip install -U discord.py + python3 -m pip install -U discord.py-self # Windows - py -3 -m pip install -U discord.py + py -3 -m pip install -U discord.py-self Otherwise to get voice support you should run the following command: .. code:: sh # Linux/macOS - python3 -m pip install -U "discord.py[voice]" + python3 -m pip install -U "discord.py-self[voice]" # Windows - py -3 -m pip install -U discord.py[voice] + py -3 -m pip install -U discord.py-self[voice] To install the development version, do the following: .. code:: sh - $ git clone https://github.com/Rapptz/discord.py - $ cd discord.py + $ git clone https://github.com/dolfies/discord.py-self + $ cd discord.py-self $ python3 -m pip install -U .[voice] @@ -77,8 +77,8 @@ Quick Example print('Logged on as', self.user) async def on_message(self, message): - # don't respond to ourselves - if message.author == self.user: + # only respond to ourselves + if message.author != self.user: return if message.content == 'ping': @@ -95,7 +95,7 @@ Bot Example import discord from discord.ext import commands - bot = commands.Bot(command_prefix='>') + bot = commands.Bot(command_prefix='>', self_bot=True) @bot.command() async def ping(ctx): @@ -108,6 +108,6 @@ You can find more examples in the examples directory. Links ------ -- `Documentation `_ -- `Official Discord Server `_ -- `Discord API `_ +- `Documentation (WIP) `_ +- `Project updates `_ +- `Discussion & support `_ diff --git a/examples/basic_bot.py b/examples/basic_bot.py index cbb8a8a11..af5a803f2 100644 --- a/examples/basic_bot.py +++ b/examples/basic_bot.py @@ -1,5 +1,3 @@ -# This example requires the 'members' and 'message_content' privileged intents - import discord from discord.ext import commands import random @@ -9,11 +7,7 @@ module. There are a number of utility commands being showcased here.''' -intents = discord.Intents.default() -intents.members = True -intents.message_content = True - -bot = commands.Bot(command_prefix='?', description=description, intents=intents) +bot = commands.Bot(command_prefix='?', description=description, self_bot=True) @bot.event async def on_ready(): diff --git a/examples/basic_voice.py b/examples/basic_voice.py index bae1174a5..9f47c77aa 100644 --- a/examples/basic_voice.py +++ b/examples/basic_voice.py @@ -1,5 +1,3 @@ -# This example requires the 'message_content' privileged intent to function. - import asyncio import discord @@ -125,12 +123,8 @@ class Music(commands.Cog): elif ctx.voice_client.is_playing(): ctx.voice_client.stop() -intents = discord.Intents.default() -intents.message_content = True - bot = commands.Bot(command_prefix=commands.when_mentioned_or("!"), - description='Relatively simple music bot example', - intents=intents) + description='Relatively simple music bot example') @bot.event async def on_ready(): diff --git a/examples/converters.py b/examples/converters.py index efd63edec..7b0f17aed 100644 --- a/examples/converters.py +++ b/examples/converters.py @@ -1,16 +1,9 @@ -# This example requires the 'members' privileged intent to use the Member converter. -# This example also requires the 'message_content' privileged intent to function. - import typing import discord from discord.ext import commands -intents = discord.Intents.default() -intents.members = True -intents.message_content = True - -bot = commands.Bot('!', intents=intents) +bot = commands.Bot('!', self_bot=True) @bot.command() diff --git a/examples/custom_context.py b/examples/custom_context.py index c9fcc4724..45450bd33 100644 --- a/examples/custom_context.py +++ b/examples/custom_context.py @@ -1,6 +1,3 @@ -# This example requires the 'message_content' privileged intent to function. - - import random import discord @@ -30,11 +27,8 @@ class MyBot(commands.Bot): # subclass to the super() method, which tells the bot to # use the new MyContext class return await super().get_context(message, cls=cls) - -intents = discord.Intents.default() -intents.message_content = True -bot = MyBot(command_prefix='!', intents=intents) +bot = MyBot(command_prefix='!', self_bot=True) @bot.command() async def guess(ctx, number: int): diff --git a/examples/deleted.py b/examples/deleted.py index 25f6765e4..6e0c203df 100644 --- a/examples/deleted.py +++ b/examples/deleted.py @@ -1,5 +1,3 @@ -# This example requires the 'message_content' privileged intent to function. - import discord class MyClient(discord.Client): @@ -19,8 +17,5 @@ class MyClient(discord.Client): msg = f'{message.author} has deleted the message: {message.content}' await message.channel.send(msg) -intents = discord.Intents.default() -intents.message_content = True - -client = MyClient(intents=intents) +client = MyClient() client.run('token') diff --git a/examples/edits.py b/examples/edits.py index 9227609f3..367860461 100644 --- a/examples/edits.py +++ b/examples/edits.py @@ -1,5 +1,3 @@ -# This example requires the 'message_content' privileged intent to function. - import discord import asyncio @@ -18,8 +16,5 @@ class MyClient(discord.Client): msg = f'**{before.author}** edited their message:\n{before.content} -> {after.content}' await before.channel.send(msg) -intents = discord.Intents.default() -intents.message_content = True - -client = MyClient(intents=intents) +client = MyClient() client.run('token') diff --git a/examples/guessing_game.py b/examples/guessing_game.py index 3574a4b3a..a6a1113cd 100644 --- a/examples/guessing_game.py +++ b/examples/guessing_game.py @@ -1,5 +1,3 @@ -# This example requires the 'message_content' privileged intent to function. - import discord import random import asyncio @@ -10,8 +8,8 @@ class MyClient(discord.Client): print('------') async def on_message(self, message): - # we do not want the bot to reply to itself - if message.author.id == self.user.id: + # we do not want the bot to reply to other people + if message.author.id != self.user.id: return if message.content.startswith('$guess'): @@ -32,8 +30,5 @@ class MyClient(discord.Client): else: await message.channel.send(f'Oops. It is actually {answer}.') -intents = discord.Intents.default() -intents.message_content = True - -client = MyClient(intents=intents) +client = MyClient() client.run('token') diff --git a/examples/new_member.py b/examples/new_member.py index 7081639b9..954750cfa 100644 --- a/examples/new_member.py +++ b/examples/new_member.py @@ -1,5 +1,3 @@ -# This example requires the 'members' privileged intents - import discord class MyClient(discord.Client): @@ -14,8 +12,5 @@ class MyClient(discord.Client): await guild.system_channel.send(to_send) -intents = discord.Intents.default() -intents.members = True - -client = MyClient(intents=intents) +client = MyClient() client.run('token') diff --git a/examples/reaction_roles.py b/examples/reaction_roles.py index b58e7f744..3f2109e87 100644 --- a/examples/reaction_roles.py +++ b/examples/reaction_roles.py @@ -1,5 +1,3 @@ -# This example requires the 'members' privileged intents - import discord class MyClient(discord.Client): @@ -78,8 +76,5 @@ class MyClient(discord.Client): # If we want to do something in case of errors we'd do it here. pass -intents = discord.Intents.default() -intents.members = True - -client = MyClient(intents=intents) +client = MyClient() client.run('token') diff --git a/examples/reply.py b/examples/reply.py index f7b71a655..b35ac669a 100644 --- a/examples/reply.py +++ b/examples/reply.py @@ -1,5 +1,3 @@ -# This example requires the 'message_content' privileged intent to function. - import discord class MyClient(discord.Client): @@ -15,8 +13,5 @@ class MyClient(discord.Client): if message.content.startswith('!hello'): await message.reply('Hello!', mention_author=True) -intents = discord.Intents.default() -intents.message_content = True - -client = MyClient(intents=intents) +client = MyClient() client.run('token') diff --git a/examples/secret.py b/examples/secret.py index 1aafa176e..faf48f535 100644 --- a/examples/secret.py +++ b/examples/secret.py @@ -1,14 +1,9 @@ -# This example requires the 'message_content' privileged intent to function. - import typing import discord from discord.ext import commands -intents = discord.Intents.default() -intents.message_content = True - -bot = commands.Bot(command_prefix=commands.when_mentioned, description="Nothing to see here!", intents=intents) +bot = commands.Bot(command_prefix=commands.when_mentioned, description="Nothing to see here!", self_bot=True) # the `hidden` keyword argument hides it from the help command. @bot.group(hidden=True) diff --git a/examples/views/confirm.py b/examples/views/confirm.py deleted file mode 100644 index cc1164800..000000000 --- a/examples/views/confirm.py +++ /dev/null @@ -1,62 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -from discord.ext import commands - -import discord - - -class Bot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - - async def on_ready(self): - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - - -# Define a simple View that gives us a confirmation menu -class Confirm(discord.ui.View): - def __init__(self): - super().__init__() - self.value = None - - # When the confirm button is pressed, set the inner value to `True` and - # stop the View from listening to more input. - # We also send the user an ephemeral message that we're confirming their choice. - @discord.ui.button(label='Confirm', style=discord.ButtonStyle.green) - async def confirm(self, button: discord.ui.Button, interaction: discord.Interaction): - await interaction.response.send_message('Confirming', ephemeral=True) - self.value = True - self.stop() - - # This one is similar to the confirmation button except sets the inner value to `False` - @discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey) - async def cancel(self, button: discord.ui.Button, interaction: discord.Interaction): - await interaction.response.send_message('Cancelling', ephemeral=True) - self.value = False - self.stop() - - -bot = Bot() - - -@bot.command() -async def ask(ctx: commands.Context): - """Asks the user a question to confirm something.""" - # We create the view and assign it to a variable so we can wait for it later. - view = Confirm() - await ctx.send('Do you want to continue?', view=view) - # Wait for the View to stop listening for input... - await view.wait() - if view.value is None: - print('Timed out...') - elif view.value: - print('Confirmed...') - else: - print('Cancelled...') - - -bot.run('token') diff --git a/examples/views/counter.py b/examples/views/counter.py deleted file mode 100644 index 9771611ce..000000000 --- a/examples/views/counter.py +++ /dev/null @@ -1,48 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -from discord.ext import commands - -import discord - - -class CounterBot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - - async def on_ready(self): - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - - -# Define a simple View that gives us a counter button -class Counter(discord.ui.View): - - # Define the actual button - # When pressed, this increments the number displayed until it hits 5. - # When it hits 5, the counter button is disabled and it turns green. - # note: The name of the function does not matter to the library - @discord.ui.button(label='0', style=discord.ButtonStyle.red) - async def count(self, button: discord.ui.Button, interaction: discord.Interaction): - number = int(button.label) if button.label else 0 - if number + 1 >= 5: - button.style = discord.ButtonStyle.green - button.disabled = True - button.label = str(number + 1) - - # Make sure to update the message with our updated selves - await interaction.response.edit_message(view=self) - - -bot = CounterBot() - - -@bot.command() -async def counter(ctx: commands.Context): - """Starts a counter for pressing.""" - await ctx.send('Press!', view=Counter()) - - -bot.run('token') diff --git a/examples/views/dropdown.py b/examples/views/dropdown.py deleted file mode 100644 index 308a415b0..000000000 --- a/examples/views/dropdown.py +++ /dev/null @@ -1,68 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -import typing - -import discord -from discord.ext import commands - -# Defines a custom Select containing colour options -# that the user can choose. The callback function -# of this class is called when the user changes their choice -class Dropdown(discord.ui.Select): - def __init__(self): - - # Set the options that will be presented inside the dropdown - options = [ - discord.SelectOption(label='Red', description='Your favourite colour is red', emoji='🟥'), - discord.SelectOption(label='Green', description='Your favourite colour is green', emoji='🟩'), - discord.SelectOption(label='Blue', description='Your favourite colour is blue', emoji='🟦') - ] - - # The placeholder is what will be shown when no option is chosen - # The min and max values indicate we can only pick one of the three options - # The options parameter defines the dropdown options. We defined this above - super().__init__(placeholder='Choose your favourite colour...', min_values=1, max_values=1, options=options) - - async def callback(self, interaction: discord.Interaction): - # Use the interaction object to send a response message containing - # the user's favourite colour or choice. The self object refers to the - # Select object, and the values attribute gets a list of the user's - # selected options. We only want the first one. - await interaction.response.send_message(f'Your favourite colour is {self.values[0]}') - - -class DropdownView(discord.ui.View): - def __init__(self): - super().__init__() - - # Adds the dropdown to our view object. - self.add_item(Dropdown()) - - -class Bot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - - async def on_ready(self): - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - - -bot = Bot() - - -@bot.command() -async def colour(ctx): - """Sends a message with our dropdown containing colours""" - - # Create the view containing our dropdown - view = DropdownView() - - # Sending a message containing our view - await ctx.send('Pick your favourite colour:', view=view) - - -bot.run('token') diff --git a/examples/views/ephemeral.py b/examples/views/ephemeral.py deleted file mode 100644 index e0c197c6b..000000000 --- a/examples/views/ephemeral.py +++ /dev/null @@ -1,52 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -from discord.ext import commands - -import discord - -class EphemeralCounterBot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - - async def on_ready(self): - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - -# Define a simple View that gives us a counter button -class Counter(discord.ui.View): - - # Define the actual button - # When pressed, this increments the number displayed until it hits 5. - # When it hits 5, the counter button is disabled and it turns green. - # note: The name of the function does not matter to the library - @discord.ui.button(label='0', style=discord.ButtonStyle.red) - async def count(self, button: discord.ui.Button, interaction: discord.Interaction): - number = int(button.label) if button.label else 0 - if number + 1 >= 5: - button.style = discord.ButtonStyle.green - button.disabled = True - button.label = str(number + 1) - - # Make sure to update the message with our updated selves - await interaction.response.edit_message(view=self) - -# Define a View that will give us our own personal counter button -class EphemeralCounter(discord.ui.View): - # When this button is pressed, it will respond with a Counter view that will - # give the button presser their own personal button they can press 5 times. - @discord.ui.button(label='Click', style=discord.ButtonStyle.blurple) - async def receive(self, button: discord.ui.Button, interaction: discord.Interaction): - # ephemeral=True makes the message hidden from everyone except the button presser - await interaction.response.send_message('Enjoy!', view=Counter(), ephemeral=True) - -bot = EphemeralCounterBot() - -@bot.command() -async def counter(ctx: commands.Context): - """Starts a counter for pressing.""" - await ctx.send('Press!', view=EphemeralCounter()) - -bot.run('token') diff --git a/examples/views/link.py b/examples/views/link.py deleted file mode 100644 index dfbcbd31e..000000000 --- a/examples/views/link.py +++ /dev/null @@ -1,44 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -from discord.ext import commands - -import discord -from urllib.parse import quote_plus - -class GoogleBot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - - async def on_ready(self): - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - - -# Define a simple View that gives us a google link button. -# We take in `query` as the query that the command author requests for -class Google(discord.ui.View): - def __init__(self, query: str): - super().__init__() - # we need to quote the query string to make a valid url. Discord will raise an error if it isn't valid. - query = quote_plus(query) - url = f'https://www.google.com/search?q={query}' - - # Link buttons cannot be made with the decorator - # Therefore we have to manually create one. - # We add the quoted url to the button, and add the button to the view. - self.add_item(discord.ui.Button(label='Click Here', url=url)) - - -bot = GoogleBot() - - -@bot.command() -async def google(ctx: commands.Context, *, query: str): - """Returns a google link for a query""" - await ctx.send(f'Google Result for: `{query}`', view=Google(query)) - - -bot.run('token') diff --git a/examples/views/persistent.py b/examples/views/persistent.py deleted file mode 100644 index 1f44ab7be..000000000 --- a/examples/views/persistent.py +++ /dev/null @@ -1,68 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -from discord.ext import commands -import discord - - -# Define a simple View that persists between bot restarts -# In order a view to persist between restarts it needs to meet the following conditions: -# 1) The timeout of the View has to be set to None -# 2) Every item in the View has to have a custom_id set -# It is recommended that the custom_id be sufficiently unique to -# prevent conflicts with other buttons the bot sends. -# For this example the custom_id is prefixed with the name of the bot. -# Note that custom_ids can only be up to 100 characters long. -class PersistentView(discord.ui.View): - def __init__(self): - super().__init__(timeout=None) - - @discord.ui.button(label='Green', style=discord.ButtonStyle.green, custom_id='persistent_view:green') - async def green(self, button: discord.ui.Button, interaction: discord.Interaction): - await interaction.response.send_message('This is green.', ephemeral=True) - - @discord.ui.button(label='Red', style=discord.ButtonStyle.red, custom_id='persistent_view:red') - async def red(self, button: discord.ui.Button, interaction: discord.Interaction): - await interaction.response.send_message('This is red.', ephemeral=True) - - @discord.ui.button(label='Grey', style=discord.ButtonStyle.grey, custom_id='persistent_view:grey') - async def grey(self, button: discord.ui.Button, interaction: discord.Interaction): - await interaction.response.send_message('This is grey.', ephemeral=True) - - -class PersistentViewBot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - self.persistent_views_added = False - - async def on_ready(self): - if not self.persistent_views_added: - # Register the persistent view for listening here. - # Note that this does not send the view to any message. - # In order to do this you need to first send a message with the View, which is shown below. - # If you have the message_id you can also pass it as a keyword argument, but for this example - # we don't have one. - self.add_view(PersistentView()) - self.persistent_views_added = True - - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - - -bot = PersistentViewBot() - - -@bot.command() -@commands.is_owner() -async def prepare(ctx: commands.Context): - """Starts a persistent view.""" - # In order for a persistent view to be listened to, it needs to be sent to an actual message. - # Call this method once just to store it somewhere. - # In a more complicated program you might fetch the message_id from a database for use later. - # However this is outside of the scope of this simple example. - await ctx.send("What's your favourite colour?", view=PersistentView()) - - -bot.run('token') diff --git a/examples/views/tic_tac_toe.py b/examples/views/tic_tac_toe.py deleted file mode 100644 index 0e2960cd5..000000000 --- a/examples/views/tic_tac_toe.py +++ /dev/null @@ -1,144 +0,0 @@ -# This example requires the 'message_content' privileged intent to function. - -from typing import List -from discord.ext import commands -import discord - -# Defines a custom button that contains the logic of the game. -# The ['TicTacToe'] bit is for type hinting purposes to tell your IDE or linter -# what the type of `self.view` is. It is not required. -class TicTacToeButton(discord.ui.Button['TicTacToe']): - def __init__(self, x: int, y: int): - # A label is required, but we don't need one so a zero-width space is used - # The row parameter tells the View which row to place the button under. - # A View can only contain up to 5 rows -- each row can only have 5 buttons. - # Since a Tic Tac Toe grid is 3x3 that means we have 3 rows and 3 columns. - super().__init__(style=discord.ButtonStyle.secondary, label='\u200b', row=y) - self.x = x - self.y = y - - # This function is called whenever this particular button is pressed - # This is part of the "meat" of the game logic - async def callback(self, interaction: discord.Interaction): - assert self.view is not None - view: TicTacToe = self.view - state = view.board[self.y][self.x] - if state in (view.X, view.O): - return - - if view.current_player == view.X: - self.style = discord.ButtonStyle.danger - self.label = 'X' - self.disabled = True - view.board[self.y][self.x] = view.X - view.current_player = view.O - content = "It is now O's turn" - else: - self.style = discord.ButtonStyle.success - self.label = 'O' - self.disabled = True - view.board[self.y][self.x] = view.O - view.current_player = view.X - content = "It is now X's turn" - - winner = view.check_board_winner() - if winner is not None: - if winner == view.X: - content = 'X won!' - elif winner == view.O: - content = 'O won!' - else: - content = "It's a tie!" - - for child in view.children: - child.disabled = True - - view.stop() - - await interaction.response.edit_message(content=content, view=view) - - -# This is our actual board View -class TicTacToe(discord.ui.View): - # This tells the IDE or linter that all our children will be TicTacToeButtons - # This is not required - children: List[TicTacToeButton] - X = -1 - O = 1 - Tie = 2 - - def __init__(self): - super().__init__() - self.current_player = self.X - self.board = [ - [0, 0, 0], - [0, 0, 0], - [0, 0, 0], - ] - - # Our board is made up of 3 by 3 TicTacToeButtons - # The TicTacToeButton maintains the callbacks and helps steer - # the actual game. - for x in range(3): - for y in range(3): - self.add_item(TicTacToeButton(x, y)) - - # This method checks for the board winner -- it is used by the TicTacToeButton - def check_board_winner(self): - for across in self.board: - value = sum(across) - if value == 3: - return self.O - elif value == -3: - return self.X - - # Check vertical - for line in range(3): - value = self.board[0][line] + self.board[1][line] + self.board[2][line] - if value == 3: - return self.O - elif value == -3: - return self.X - - # Check diagonals - diag = self.board[0][2] + self.board[1][1] + self.board[2][0] - if diag == 3: - return self.O - elif diag == -3: - return self.X - - diag = self.board[0][0] + self.board[1][1] + self.board[2][2] - if diag == 3: - return self.O - elif diag == -3: - return self.X - - # If we're here, we need to check if a tie was made - if all(i != 0 for row in self.board for i in row): - return self.Tie - - return None - - -class TicTacToeBot(commands.Bot): - def __init__(self): - intents = discord.Intents.default() - intents.message_content = True - - super().__init__(command_prefix=commands.when_mentioned_or('$'), intents=intents) - - async def on_ready(self): - print(f'Logged in as {self.user} (ID: {self.user.id})') - print('------') - - -bot = TicTacToeBot() - - -@bot.command() -async def tic(ctx: commands.Context): - """Starts a tic-tac-toe game with yourself.""" - await ctx.send('Tic Tac Toe: X goes first', view=TicTacToe()) - - -bot.run('token') diff --git a/pyproject.toml b/pyproject.toml index db2927f26..db235ba9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,6 @@ line_length = 125 include = [ "discord", "discord/types", - "discord/ui", "discord/ext", "discord/ext/commands", "discord/ext/tasks", diff --git a/setup.py b/setup.py index 7052bb4de..d6073a6e1 100644 --- a/setup.py +++ b/setup.py @@ -58,8 +58,10 @@ setup(name='discord.py-self', author='Dolfies', url='https://github.com/dolfies/discord.py-self', project_urls={ - "Documentation": "https://dolf.ml/discord.py-self", + "Documentation": "https://discordpy-self.readthedocs.io/en/latest/", "Issue tracker": "https://github.com/dolfies/discord.py-self/issues", + "Project updates": "https://t.me/dpy_self", + "Discussion & support": "https://t.me/dpy_self_discussions", }, version=version, packages=find_packages() + ['discord.ext.commands', 'discord.ext.tasks'],