From 1c0388d0cffcf3768d37b0b873fbb5dea0ce5221 Mon Sep 17 00:00:00 2001 From: wizzdom Date: Fri, 25 Oct 2024 23:41:56 +0100 Subject: [PATCH] use unix line endings (#47) --- .gitattributes | 1 + requirements.txt | 10 +- src/__main__.py | 12 +-- src/bot.py | 72 +++++++------- src/config.py | 20 ++-- src/extensions/boosts.py | 116 ++++++++++++----------- src/extensions/hello_world.py | 174 ++++++++++++++++++---------------- src/extensions/uptime.py | 58 ++++++------ src/extensions/user_roles.py | 28 ++++-- 9 files changed, 260 insertions(+), 231 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/requirements.txt b/requirements.txt index dc1dbf7..4bb7724 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -hikari==2.0.0.dev122 -hikari-arc==1.1.0 -ruff==0.2.0 -pre-commit==3.6.0 -python-dotenv==1.0.1 \ No newline at end of file +hikari==2.0.0.dev122 +hikari-arc==1.1.0 +ruff==0.2.0 +pre-commit==3.6.0 +python-dotenv==1.0.1 diff --git a/src/__main__.py b/src/__main__.py index 4db49ff..41c39ff 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -1,6 +1,6 @@ -from hikari import Activity, ActivityType - -from src.bot import bot - -if __name__ == "__main__": - bot.run(activity=Activity(name="Webgroup issues", type=ActivityType.WATCHING)) +from hikari import Activity, ActivityType + +from src.bot import bot + +if __name__ == "__main__": + bot.run(activity=Activity(name="Webgroup issues", type=ActivityType.WATCHING)) diff --git a/src/bot.py b/src/bot.py index e078fcf..1f8a241 100644 --- a/src/bot.py +++ b/src/bot.py @@ -1,36 +1,36 @@ -import logging -import sys - -import arc -import hikari - -from src.config import DEBUG, TOKEN - -if TOKEN is None: - print("TOKEN environment variable not set. Exiting.") - sys.exit(1) - -bot = hikari.GatewayBot( - token=TOKEN, - banner=None, - intents=hikari.Intents.ALL_UNPRIVILEGED | hikari.Intents.MESSAGE_CONTENT, - logs="DEBUG" if DEBUG else "INFO", -) - -logging.info(f"Debug mode is {DEBUG}; You can safely ignore this.") - -client = arc.GatewayClient(bot, is_dm_enabled=False) -client.load_extensions_from("./src/extensions/") - - -@client.set_error_handler -async def error_handler(ctx: arc.GatewayContext, exc: Exception) -> None: - if DEBUG: - message = f"```{exc}```" - else: - message = "If this persists, create an issue at ." - - await ctx.respond(f"❌ Blockbot encountered an unhandled exception. {message}") - logging.error(exc) - - raise exc +import logging +import sys + +import arc +import hikari + +from src.config import DEBUG, TOKEN + +if TOKEN is None: + print("TOKEN environment variable not set. Exiting.") + sys.exit(1) + +bot = hikari.GatewayBot( + token=TOKEN, + banner=None, + intents=hikari.Intents.ALL_UNPRIVILEGED | hikari.Intents.MESSAGE_CONTENT, + logs="DEBUG" if DEBUG else "INFO", +) + +logging.info(f"Debug mode is {DEBUG}; You can safely ignore this.") + +client = arc.GatewayClient(bot, is_dm_enabled=False) +client.load_extensions_from("./src/extensions/") + + +@client.set_error_handler +async def error_handler(ctx: arc.GatewayContext, exc: Exception) -> None: + if DEBUG: + message = f"```{exc}```" + else: + message = "If this persists, create an issue at ." + + await ctx.respond(f"❌ Blockbot encountered an unhandled exception. {message}") + logging.error(exc) + + raise exc diff --git a/src/config.py b/src/config.py index 42cc71a..e4d66f9 100644 --- a/src/config.py +++ b/src/config.py @@ -1,10 +1,10 @@ -import os - -from dotenv import load_dotenv - -load_dotenv() - -TOKEN = os.environ.get("TOKEN") # required -DEBUG = os.environ.get("DEBUG", False) - -CHANNEL_IDS: dict[str, int] = {"lobby": 627542044390457350} +import os + +from dotenv import load_dotenv + +load_dotenv() + +TOKEN = os.environ.get("TOKEN") # required +DEBUG = os.environ.get("DEBUG", False) + +CHANNEL_IDS: dict[str, int] = {"lobby": 627542044390457350} diff --git a/src/extensions/boosts.py b/src/extensions/boosts.py index 5fc6c7d..6222c48 100644 --- a/src/extensions/boosts.py +++ b/src/extensions/boosts.py @@ -1,56 +1,60 @@ -import arc -import hikari - -from src.config import CHANNEL_IDS -from src.utils import get_guild - -plugin = arc.GatewayPlugin(name="Boosts") - -BOOST_TIERS: list[hikari.MessageType] = [ - hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1, - hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2, - hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3, -] - -BOOST_MESSAGE_TYPES: list[hikari.MessageType] = BOOST_TIERS + [hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION] - - -def build_boost_message( - message_type: hikari.MessageType | int, - number_of_boosts: str | None, - booster_user: hikari.Member, - guild: hikari.Guild, -) -> str: - assert message_type in BOOST_MESSAGE_TYPES - - base_message = f"{booster_user.display_name} just boosted the server" - multiple_boosts_message = f" **{number_of_boosts}** times" if number_of_boosts else "" - - message = base_message + multiple_boosts_message + "!" - - if message_type in BOOST_TIERS: - count = BOOST_TIERS.index(message_type) + 1 - message += f"\n{guild.name} has reached **Level {count}!**" - - return message - - -@plugin.listen() -async def on_message(event: hikari.GuildMessageCreateEvent) -> None: - if event.message.type not in BOOST_MESSAGE_TYPES: - return - - assert event.member is not None - message = build_boost_message( - event.message.type, - number_of_boosts=event.content, - booster_user=event.member, - guild=await get_guild(plugin.client, event), - ) - - await plugin.client.rest.create_message(CHANNEL_IDS["lobby"], content=message) - - -@arc.loader -def loader(client: arc.GatewayClient) -> None: - client.add_plugin(plugin) +import arc +import hikari + +from src.config import CHANNEL_IDS +from src.utils import get_guild + +plugin = arc.GatewayPlugin(name="Boosts") + +BOOST_TIERS: list[hikari.MessageType] = [ + hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1, + hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2, + hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3, +] + +BOOST_MESSAGE_TYPES: list[hikari.MessageType] = BOOST_TIERS + [ + hikari.MessageType.USER_PREMIUM_GUILD_SUBSCRIPTION +] + + +def build_boost_message( + message_type: hikari.MessageType | int, + number_of_boosts: str | None, + booster_user: hikari.Member, + guild: hikari.Guild, +) -> str: + assert message_type in BOOST_MESSAGE_TYPES + + base_message = f"{booster_user.display_name} just boosted the server" + multiple_boosts_message = ( + f" **{number_of_boosts}** times" if number_of_boosts else "" + ) + + message = base_message + multiple_boosts_message + "!" + + if message_type in BOOST_TIERS: + count = BOOST_TIERS.index(message_type) + 1 + message += f"\n{guild.name} has reached **Level {count}!**" + + return message + + +@plugin.listen() +async def on_message(event: hikari.GuildMessageCreateEvent) -> None: + if event.message.type not in BOOST_MESSAGE_TYPES: + return + + assert event.member is not None + message = build_boost_message( + event.message.type, + number_of_boosts=event.content, + booster_user=event.member, + guild=await get_guild(plugin.client, event), + ) + + await plugin.client.rest.create_message(CHANNEL_IDS["lobby"], content=message) + + +@arc.loader +def loader(client: arc.GatewayClient) -> None: + client.add_plugin(plugin) diff --git a/src/extensions/hello_world.py b/src/extensions/hello_world.py index be97d92..0de108a 100644 --- a/src/extensions/hello_world.py +++ b/src/extensions/hello_world.py @@ -1,83 +1,91 @@ -""" -Example extension with simple commands -""" - -import arc -import hikari - -plugin = arc.GatewayPlugin(name="Hello World") - - -@plugin.include -@arc.slash_command("hello", "Say hello!") -async def hello(ctx: arc.GatewayContext) -> None: - """A simple hello world command""" - await ctx.respond("Hello from hikari!") - - -group = plugin.include_slash_group("base_command", "A base command, to expand on") - - -@group.include -@arc.slash_subcommand("sub_command", "A sub command, to expand on") -async def sub_command(ctx: arc.GatewayContext) -> None: - """A simple sub command""" - await ctx.respond("Hello, world! This is a sub command") - - -@plugin.include -@arc.slash_command("options", "A command with options") -async def options( - ctx: arc.GatewayContext, - option_str: arc.Option[str, arc.StrParams("A string option")], - option_int: arc.Option[int, arc.IntParams("An integer option")], - option_attachment: arc.Option[hikari.Attachment, arc.AttachmentParams("An attachment option")], -) -> None: - """A command with lots of options""" - embed = hikari.Embed(title="There are a lot of options here", description="Maybe too many", colour=0x5865F2) - embed.set_image(option_attachment) - embed.add_field("String option", option_str, inline=False) - embed.add_field("Integer option", str(option_int), inline=False) - await ctx.respond(embed=embed) - - -@plugin.include -@arc.slash_command("components", "A command with components") -async def components(ctx: arc.GatewayContext) -> None: - """A command with components""" - builder = ctx.client.rest.build_message_action_row() - select_menu = builder.add_text_menu("select_me", placeholder="I wonder what this does", min_values=1, max_values=2) - for opt in ("Select me!", "No, select me!", "Select me too!"): - select_menu.add_option(opt, opt) - - button = ctx.client.rest.build_message_action_row().add_interactive_button( - hikari.ButtonStyle.PRIMARY, "click_me", label="Click me!" - ) - - await ctx.respond("Here are some components", components=[builder, button]) - - -@plugin.listen() -async def on_interaction(event: hikari.InteractionCreateEvent) -> None: - interaction = event.interaction - - # Discussions are underway for allowing to listen for a "ComponentInteractionEvent" directly - # instead of doing this manual filtering: https://github.com/hikari-py/hikari/issues/1777 - if not isinstance(interaction, hikari.ComponentInteraction): - return - - if interaction.custom_id == "click_me": - await interaction.create_initial_response( - hikari.ResponseType.MESSAGE_CREATE, - f"{interaction.user.mention}, you clicked me!", - ) - elif interaction.custom_id == "select_me": - await interaction.create_initial_response( - hikari.ResponseType.MESSAGE_CREATE, - f"{interaction.user.mention}, you selected {' '.join(interaction.values)}", - ) - - -@arc.loader -def loader(client: arc.GatewayClient) -> None: - client.add_plugin(plugin) +""" +Example extension with simple commands +""" + +import arc +import hikari + +plugin = arc.GatewayPlugin(name="Hello World") + + +@plugin.include +@arc.slash_command("hello", "Say hello!") +async def hello(ctx: arc.GatewayContext) -> None: + """A simple hello world command""" + await ctx.respond("Hello from hikari!") + + +group = plugin.include_slash_group("base_command", "A base command, to expand on") + + +@group.include +@arc.slash_subcommand("sub_command", "A sub command, to expand on") +async def sub_command(ctx: arc.GatewayContext) -> None: + """A simple sub command""" + await ctx.respond("Hello, world! This is a sub command") + + +@plugin.include +@arc.slash_command("options", "A command with options") +async def options( + ctx: arc.GatewayContext, + option_str: arc.Option[str, arc.StrParams("A string option")], + option_int: arc.Option[int, arc.IntParams("An integer option")], + option_attachment: arc.Option[ + hikari.Attachment, arc.AttachmentParams("An attachment option") + ], +) -> None: + """A command with lots of options""" + embed = hikari.Embed( + title="There are a lot of options here", + description="Maybe too many", + colour=0x5865F2, + ) + embed.set_image(option_attachment) + embed.add_field("String option", option_str, inline=False) + embed.add_field("Integer option", str(option_int), inline=False) + await ctx.respond(embed=embed) + + +@plugin.include +@arc.slash_command("components", "A command with components") +async def components(ctx: arc.GatewayContext) -> None: + """A command with components""" + builder = ctx.client.rest.build_message_action_row() + select_menu = builder.add_text_menu( + "select_me", placeholder="I wonder what this does", min_values=1, max_values=2 + ) + for opt in ("Select me!", "No, select me!", "Select me too!"): + select_menu.add_option(opt, opt) + + button = ctx.client.rest.build_message_action_row().add_interactive_button( + hikari.ButtonStyle.PRIMARY, "click_me", label="Click me!" + ) + + await ctx.respond("Here are some components", components=[builder, button]) + + +@plugin.listen() +async def on_interaction(event: hikari.InteractionCreateEvent) -> None: + interaction = event.interaction + + # Discussions are underway for allowing to listen for a "ComponentInteractionEvent" directly + # instead of doing this manual filtering: https://github.com/hikari-py/hikari/issues/1777 + if not isinstance(interaction, hikari.ComponentInteraction): + return + + if interaction.custom_id == "click_me": + await interaction.create_initial_response( + hikari.ResponseType.MESSAGE_CREATE, + f"{interaction.user.mention}, you clicked me!", + ) + elif interaction.custom_id == "select_me": + await interaction.create_initial_response( + hikari.ResponseType.MESSAGE_CREATE, + f"{interaction.user.mention}, you selected {' '.join(interaction.values)}", + ) + + +@arc.loader +def loader(client: arc.GatewayClient) -> None: + client.add_plugin(plugin) diff --git a/src/extensions/uptime.py b/src/extensions/uptime.py index c3ffba8..5afa95a 100644 --- a/src/extensions/uptime.py +++ b/src/extensions/uptime.py @@ -1,29 +1,29 @@ -from datetime import datetime - -import arc - -start_time = datetime.now() - -plugin = arc.GatewayPlugin("Blockbot Uptime") - - -@plugin.include -@arc.slash_command("uptime", "Show formatted uptime of Blockbot") -async def uptime(ctx: arc.GatewayContext) -> None: - up_time = datetime.now() - start_time - d = up_time.days - h, ms = divmod(up_time.seconds, 3600) - m, s = divmod(ms, 60) - - def format(val: int, s: str): - return f"{val} {s}{'s' if val != 1 else ''}" - - message_parts = [(d, "day"), (h, "hour"), (m, "minute"), (s, "second")] - formatted_parts = [format(val, str) for val, str in message_parts if val] - - await ctx.respond(f"Uptime: **{', '.join(formatted_parts)}**") - - -@arc.loader -def loader(client: arc.GatewayClient) -> None: - client.add_plugin(plugin) +from datetime import datetime + +import arc + +start_time = datetime.now() + +plugin = arc.GatewayPlugin("Blockbot Uptime") + + +@plugin.include +@arc.slash_command("uptime", "Show formatted uptime of Blockbot") +async def uptime(ctx: arc.GatewayContext) -> None: + up_time = datetime.now() - start_time + d = up_time.days + h, ms = divmod(up_time.seconds, 3600) + m, s = divmod(ms, 60) + + def format(val: int, s: str): + return f"{val} {s}{'s' if val != 1 else ''}" + + message_parts = [(d, "day"), (h, "hour"), (m, "minute"), (s, "second")] + formatted_parts = [format(val, str) for val, str in message_parts if val] + + await ctx.respond(f"Uptime: **{', '.join(formatted_parts)}**") + + +@arc.loader +def loader(client: arc.GatewayClient) -> None: + client.add_plugin(plugin) diff --git a/src/extensions/user_roles.py b/src/extensions/user_roles.py index 4c14d53..7653e3e 100644 --- a/src/extensions/user_roles.py +++ b/src/extensions/user_roles.py @@ -25,11 +25,19 @@ async def add_role( assert ctx.member if int(role) in ctx.member.role_ids: - await ctx.respond(f"You already have the {role_mention(role)} role.", flags=hikari.MessageFlag.EPHEMERAL) + await ctx.respond( + f"You already have the {role_mention(role)} role.", + flags=hikari.MessageFlag.EPHEMERAL, + ) return - await ctx.client.rest.add_role_to_member(ctx.guild_id, ctx.author, int(role), reason="Self-service role.") - await ctx.respond(f"Done! Added {role_mention(role)} to your roles.", flags=hikari.MessageFlag.EPHEMERAL) + await ctx.client.rest.add_role_to_member( + ctx.guild_id, ctx.author, int(role), reason="Self-service role." + ) + await ctx.respond( + f"Done! Added {role_mention(role)} to your roles.", + flags=hikari.MessageFlag.EPHEMERAL, + ) @role.include @@ -42,13 +50,19 @@ async def remove_role( assert ctx.member if int(role) not in ctx.member.role_ids: - await ctx.respond(f"You don't have the {role_mention(role)} role.", flags=hikari.MessageFlag.EPHEMERAL) + await ctx.respond( + f"You don't have the {role_mention(role)} role.", + flags=hikari.MessageFlag.EPHEMERAL, + ) return await ctx.client.rest.remove_role_from_member( ctx.guild_id, ctx.author, int(role), reason=f"{ctx.author} removed role." ) - await ctx.respond(f"Done! Removed {role_mention(role)} from your roles.", flags=hikari.MessageFlag.EPHEMERAL) + await ctx.respond( + f"Done! Removed {role_mention(role)} from your roles.", + flags=hikari.MessageFlag.EPHEMERAL, + ) @role.set_error_handler @@ -64,7 +78,9 @@ async def role_error_handler(ctx: arc.GatewayContext, exc: Exception) -> None: return if isinstance(exc, hikari.NotFoundError): - await ctx.respond("❌ Blockbot can't find that role.", flags=hikari.MessageFlag.EPHEMERAL) + await ctx.respond( + "❌ Blockbot can't find that role.", flags=hikari.MessageFlag.EPHEMERAL + ) return raise exc