Add async start and sync run (#94)
Some checks failed
Deploy MkDocs / deploy (push) Has been cancelled

This commit is contained in:
Slipstream 2025-06-15 18:11:44 -06:00 committed by GitHub
parent 5d66eb79cc
commit 1464937f6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 75 additions and 89 deletions

View File

@ -61,13 +61,8 @@ if not token:
intents = disagreement.GatewayIntent.default() | disagreement.GatewayIntent.MESSAGE_CONTENT
client = disagreement.Client(token=token, command_prefix="!", intents=intents, mention_replies=True)
async def main() -> None:
client.add_cog(Basics(client))
await client.run()
if __name__ == "__main__":
asyncio.run(main())
client.add_cog(Basics(client))
client.run()
```
### Global Error Handling

View File

@ -159,7 +159,7 @@ class Client:
)
self._event_dispatcher: EventDispatcher = EventDispatcher(client_instance=self)
self._gateway: Optional[GatewayClient] = (
None # Initialized in run() or connect()
None # Initialized in start() or connect()
)
self.shard_count: Optional[int] = shard_count
self.gateway_max_retries: int = gateway_max_retries
@ -269,7 +269,7 @@ class Client:
assert self._gateway is not None # Should be initialized by now
retry_delay = 5 # seconds
max_retries = 5 # For initial connection attempts by Client.run, Gateway has its own internal retries for some cases.
max_retries = 5 # For initial connection attempts by Client.start, Gateway has its own internal retries for some cases.
for attempt in range(max_retries):
try:
@ -300,11 +300,10 @@ class Client:
if max_retries == 0: # If max_retries was 0, means no retries attempted
raise DisagreementException("Connection failed with 0 retries allowed.")
async def run(self) -> None:
async def start(self) -> None:
"""
A blocking call that connects the client to Discord and runs until the client is closed.
This method is a coroutine.
It handles login, Gateway connection, and keeping the connection alive.
Connect the client to Discord and run until the client is closed.
This method is a coroutine containing the main run loop logic.
"""
if self._closed:
raise DisagreementException("Client is already closed.")
@ -372,6 +371,10 @@ class Client:
if not self._closed:
await self.close()
def run(self) -> None:
"""Synchronously start the client using :func:`asyncio.run`."""
asyncio.run(self.start())
async def close(self) -> None:
"""
Closes the connection to Discord. This method is a coroutine.

View File

@ -60,13 +60,8 @@ if not token:
intents = GatewayIntent.default() | GatewayIntent.MESSAGE_CONTENT
client = Client(token=token, command_prefix="!", intents=intents, mention_replies=True)
async def main() -> None:
client.add_cog(Basics(client))
await client.run()
if __name__ == "__main__":
asyncio.run(main())
client.add_cog(Basics(client))
client.run()
```
### Global Error Handling

View File

@ -8,13 +8,8 @@ manually.
and configures the `ShardManager` automatically.
```python
import asyncio
import disagreement
bot = disagreement.AutoShardedClient(token="YOUR_TOKEN")
async def main():
await bot.run()
asyncio.run(main())
bot.run()
```

View File

@ -67,9 +67,7 @@ BOT_TOKEN = os.environ.get("DISCORD_BOT_TOKEN")
# --- Intents Configuration ---
# Define the intents your bot needs. For basic message reading and responding:
intents = (
GatewayIntent.GUILDS
| GatewayIntent.GUILD_MESSAGES
| GatewayIntent.MESSAGE_CONTENT
GatewayIntent.GUILDS | GatewayIntent.GUILD_MESSAGES | GatewayIntent.MESSAGE_CONTENT
) # MESSAGE_CONTENT is privileged!
# If you don't need message content and only react to commands/mentions,
@ -210,14 +208,14 @@ async def on_guild_available(guild: Guild):
# --- Main Execution ---
async def main():
def main():
print("Starting Disagreement Bot...")
try:
# Add the Cog to the client
client.add_cog(ExampleCog(client)) # Pass client instance to Cog constructor
# client.add_cog is synchronous, but it schedules cog.cog_load() if it's async.
await client.run()
client.run()
except AuthenticationError:
print(
"Authentication failed. Please check your bot token and ensure it's correct."
@ -232,7 +230,7 @@ async def main():
finally:
if not client.is_closed():
print("Ensuring client is closed...")
await client.close()
asyncio.run(client.close())
print("Bot has been shut down.")
@ -244,4 +242,4 @@ if __name__ == "__main__":
# if os.name == 'nt':
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())
main()

View File

@ -263,7 +263,7 @@ class ComponentCommandsCog(Cog):
)
async def main():
def main():
@client.event
async def on_ready():
if client.user:
@ -283,8 +283,8 @@ async def main():
)
client.add_cog(ComponentCommandsCog(client))
await client.run()
client.run()
if __name__ == "__main__":
asyncio.run(main())
main()

View File

@ -65,11 +65,9 @@ client.app_command_handler.add_command(user_info)
client.app_command_handler.add_command(quote)
async def main() -> None:
await client.run()
def main() -> None:
client.run()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
main()

View File

@ -27,10 +27,10 @@ intents = GatewayIntent.default() | GatewayIntent.MESSAGE_CONTENT
client = Client(token=token, command_prefix="!", intents=intents, mention_replies=True)
async def main() -> None:
def main() -> None:
client.add_cog(Basics(client))
await client.run()
client.run()
if __name__ == "__main__":
asyncio.run(main())
main()

View File

@ -230,7 +230,7 @@ class TestCog(Cog):
# --- Main Bot Script ---
async def main():
def main():
bot_token = os.getenv("DISCORD_BOT_TOKEN")
application_id = os.getenv("DISCORD_APPLICATION_ID")
@ -291,7 +291,7 @@ async def main():
client.add_cog(TestCog(client))
try:
await client.run()
client.run()
except KeyboardInterrupt:
logger.info("Bot shutting down...")
except Exception as e:
@ -300,7 +300,7 @@ async def main():
)
finally:
if not client.is_closed():
await client.close()
asyncio.run(client.close())
logger.info("Bot has been closed.")
@ -310,6 +310,6 @@ if __name__ == "__main__":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
try:
asyncio.run(main())
main()
except KeyboardInterrupt:
logger.info("Main loop interrupted. Exiting.")

View File

@ -62,9 +62,9 @@ async def on_ready():
print("------")
async def main():
await client.run()
def main():
client.run()
if __name__ == "__main__":
asyncio.run(main())
main()

View File

@ -63,6 +63,4 @@ async def on_ready():
if __name__ == "__main__":
import asyncio
asyncio.run(client.run())
client.run()

View File

@ -9,7 +9,15 @@ from typing import Set
if os.path.join(os.getcwd(), "examples") == os.path.dirname(os.path.abspath(__file__)):
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from disagreement import Client, GatewayIntent, Member, Message, Cog, command, CommandContext
from disagreement import (
Client,
GatewayIntent,
Member,
Message,
Cog,
command,
CommandContext,
)
try:
from dotenv import load_dotenv
@ -26,9 +34,7 @@ if not BOT_TOKEN:
sys.exit(1)
intents = (
GatewayIntent.GUILDS
| GatewayIntent.GUILD_MESSAGES
| GatewayIntent.MESSAGE_CONTENT
GatewayIntent.GUILDS | GatewayIntent.GUILD_MESSAGES | GatewayIntent.MESSAGE_CONTENT
)
client = Client(token=BOT_TOKEN, command_prefix="!", intents=intents)
@ -78,10 +84,10 @@ async def on_message(message: Message) -> None:
)
async def main() -> None:
def main() -> None:
client.add_cog(ModerationCog(client))
await client.run()
client.run()
if __name__ == "__main__":
asyncio.run(main())
main()

View File

@ -137,11 +137,11 @@ async def on_reaction_remove(reaction: Reaction, user: User | Member):
# --- Main Execution ---
async def main():
def main():
print("Starting Reaction Bot...")
try:
client.add_cog(ReactionCog(client))
await client.run()
client.run()
except AuthenticationError:
print("Authentication failed. Check your bot token.")
except Exception as e:
@ -149,9 +149,9 @@ async def main():
traceback.print_exc()
finally:
if not client.is_closed():
await client.close()
asyncio.run(client.close())
print("Bot has been shut down.")
if __name__ == "__main__":
asyncio.run(main())
main()

View File

@ -34,12 +34,12 @@ async def on_ready():
print("Shard bot ready")
async def main():
def main():
if not TOKEN:
print("DISCORD_BOT_TOKEN environment variable not set")
return
await client.run()
client.run()
if __name__ == "__main__":
asyncio.run(main())
main()

View File

@ -53,9 +53,7 @@ BOT_TOKEN = os.environ.get("DISCORD_BOT_TOKEN")
# --- Intents Configuration ---
intents = (
GatewayIntent.GUILDS
| GatewayIntent.GUILD_MESSAGES
| GatewayIntent.MESSAGE_CONTENT
GatewayIntent.GUILDS | GatewayIntent.GUILD_MESSAGES | GatewayIntent.MESSAGE_CONTENT
)
# --- Initialize the Client ---
@ -106,11 +104,11 @@ async def on_ready():
# --- Main Execution ---
async def main():
def main():
print("Starting Typing Indicator Bot...")
try:
client.add_cog(TypingCog(client))
await client.run()
client.run()
except AuthenticationError:
print("Authentication failed. Check your bot token.")
except Exception as e:
@ -118,9 +116,9 @@ async def main():
traceback.print_exc()
finally:
if not client.is_closed():
await client.close()
asyncio.run(client.close())
print("Bot has been shut down.")
if __name__ == "__main__":
asyncio.run(main())
main()