diff --git a/.env.template b/.env.template index 2b5b9091..93ef965c 100644 --- a/.env.template +++ b/.env.template @@ -45,9 +45,7 @@ API_PRIVATE_KEY= API_STATIC_TOKEN= BASE_API_URL= PANEL_API_URL= -# MC API -MC_API_URL= -MC_API_KEY= + # Cog toggles # TRUE or FALSE — controls the Reminders cog REMINDERS_ENABLED=TRUE diff --git a/cogs/Configuration.py b/cogs/Configuration.py index af444c50..7725ac9c 100644 --- a/cogs/Configuration.py +++ b/cogs/Configuration.py @@ -30,7 +30,6 @@ WhitelistVehiclesManagement, PriorityRequestConfiguration, ) -from ui.MapleCounty import MapleCountyConfiguration from utils.paginators import CustomPage, SelectPagination from utils.utils import require_settings, generator, log_command_usage @@ -985,12 +984,6 @@ async def _config(self, ctx: commands.Context): ], ) - maple_county_configuration = MapleCountyConfiguration( - bot, - ctx.author.id, - settings.get("MC", {}) - ) - pages = [] for index, view in enumerate( @@ -1006,7 +999,6 @@ async def _config(self, ctx: commands.Context): erlc_view, erm_command_log_view, priority_requests, - maple_county_configuration, ] ): corresponding_embeds = [ @@ -1121,13 +1113,6 @@ async def _config(self, ctx: commands.Context): "**Priority Channel:** This channel will be where priority requests are submitted, and where the message advising staff in regards to the priority request will be sent." ), ), - discord.Embed( - title="Maple County Integration", - color=blank_color, - description=( - "**What is the Maple County Integration?**\nThe Maple County Integration allows for ERM to communicate with the Maple County APIs, and your Maple County server. In particular, these configurations allow for configuration of various Maple County-specific supported features and settings.\n\n" - ) - ) ] embed = corresponding_embeds[index] page = CustomPage() diff --git a/cogs/MC.py b/cogs/MC.py deleted file mode 100644 index b173f6a0..00000000 --- a/cogs/MC.py +++ /dev/null @@ -1,349 +0,0 @@ -import asyncio -import datetime -import json -import re -import discord -import roblox -from discord.ext import commands -from utils.autocompletes import erlc_group_autocomplete, erlc_players_autocomplete -from roblox.thumbnails import AvatarThumbnailType - -import logging -from typing import List -from erm import admin_check, is_staff, is_management, management_predicate -from utils.paginators import CustomPage, SelectPagination -from menus import CustomModal, ReloadView, RefreshConfirmation, RiskyUsersMenu, CustomExecutionButton -import copy -from utils.constants import * -from utils.prc_api import ( - Player, - ServerStatus, - KillLog, - JoinLeaveLog, - CommandLog, - ResponseFailure, -) -import utils.prc_api as prc_api -from utils.utils import get_discord_by_roblox, get_roblox_by_username, log_command_usage, secure_logging, staff_check -from discord import app_commands -import typing - - -class MC(commands.Cog): - def __init__(self, bot: commands.Bot): - self.bot = bot - - @staticmethod - def is_mc_server_linked(): - async def predicate(ctx: commands.Context): - guild_id = ctx.guild.id - - try: - await ctx.bot.mc_api.get_server_status(guild_id) - except prc_api.ResponseFailure as exc: - error = prc_api.ServerLinkNotFound(platform="mc") - try: - error.code = exc.json_data.get("code") or exc.status_code - except json.JSONDecodeError: - pass - raise error - - return True - - return commands.check(predicate) - - async def secure_logging( - self, - guild_id, - author_id, - interpret_type: typing.Literal["Message", "Hint", "Command"], - command_string: str, - attempted: bool = False, - ): - await secure_logging( - self.bot, guild_id, author_id, interpret_type, command_string, attempted - ) - - @commands.hybrid_group(name="mc") - async def mc(self, ctx: commands.Context): - pass - - @mc.command(name="link", description="Link your Maple County server with ERM!") - @is_management() - async def mc_link(self, ctx: commands.Context, *, server_name: str): - # get the linked roblox user - roblox_id = 0 - oauth2_user = ( - await self.bot.oauth2_users.db.find_one({"discord_id": ctx.author.id}) or {} - ) - if not oauth2_user.get("roblox_id"): - # go to fallback - roblox_user = await self.bot.bloxlink.find_roblox(ctx.author.id) - if not roblox_user.get("robloxID"): - return await ctx.send( - embed=discord.Embed( - title="Not Linked", - description="You are not linked to any ROBLOX account.", - color=BLANK_COLOR, - ) - ) - roblox_id = roblox_user["robloxID"] - else: - roblox_id = oauth2_user["roblox_id"] - - try: - server_token = await self.bot.mc_api.authorize( - roblox_id, server_name, ctx.guild.id - ) - except prc_api.ResponseFailure: # yes, this is correct. - return await ctx.send( - embed=discord.Embed( - title="Server Not Found", - description="We could not find a server you own under the server name provided. Make sure you are linked with ERM by running `/link` in any server.", - color=BLANK_COLOR, - ) - ) - - await ctx.send( - embed=discord.Embed( - title=f"{self.bot.emoji_controller.get_emoji('success')} Server Linked", - description=f"Your server has been linked with the name `{server_name}`.", - color=GREEN_COLOR, - ) - ) - - @mc.command( - name="info", - description="Get information about the current players in your Maple County server.", - ) - @is_mc_server_linked() - async def mc_info(self, ctx: commands.Context): - guild_id = ctx.guild.id - - async def operate_and_reload_serverinfo( - msg: discord.Message | None, guild_id: str - ): - guild_id = int(guild_id) - status: ServerStatus = await self.bot.mc_api.get_server_status(guild_id) - players: list[Player] = await self.bot.mc_api.get_server_players(guild_id) - client = roblox.Client() - - embed1 = discord.Embed(title=f"{status.name}", color=BLANK_COLOR) - embed1.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - embed1.add_field( - name="Basic Info", - value=( - f"> **Join Code:** [{status.join_key}](https://www.roblox.com/games/start?placeId=8416011646&launchData=psjoincode%3D{status.join_key}&deep_link_value=roblox%3A%2F%2FplaceId%3D8416011646)\n" - f"> **Current Players:** {status.current_players}/{status.max_players}\n" - ), - inline=False, - ) - embed1.add_field( - name="Server Ownership", - value=( - f"> **Owner:** [{(await client.get_user(status.owner_id)).name}](https://roblox.com/users/{status.owner_id}/profile)\n" - f"> **Co-Owners:** {f', '.join([f'[{user.name}](https://roblox.com/users/{user.id}/profile)' for user in await client.get_users(status.co_owner_ids, expand=False)])}" - ), - inline=False, - ) - - embed1.add_field( - name="Staff Statistics", - value=( - f"> **Moderators:** {len(list(filter(lambda x: x.permission == 'Server Moderator', players)))}\n" - f"> **Administrators:** {len(list(filter(lambda x: x.permission == 'Server Administrator', players)))}\n" - f"> **Staff In-Game:** {len(list(filter(lambda x: x.permission != 'Normal', players)))}\n" - f"> **Staff Clocked In:** {await self.bot.shift_management.shifts.db.count_documents({'Guild': guild_id, 'EndEpoch': 0})}" - ), - inline=False, - ) - - if msg is None: - view = ReloadView( - self.bot, - ctx.author.id, - operate_and_reload_serverinfo, - [None, guild_id], - ) - msg = await ctx.send(embed=embed1, view=view) - view.message = msg - view.callback_args[0] = msg - else: - await msg.edit(embed=embed1) - - await operate_and_reload_serverinfo(None, guild_id) - - @mc.command(name="logs", description="See the Command Logs of your server.") - @is_staff() - @is_mc_server_linked() - async def mc_logs(self, ctx: commands.Context): - guild_id = ctx.guild.id - - async def operate_and_reload_commandlogs(msg, guild_id: str): - guild_id = int(guild_id) - # status: ServerStatus = await self.bot.prc_api.get_server_status(guild_id) - command_logs: list[CommandLog] = await self.bot.mc_api.fetch_server_logs( - guild_id - ) - embed = discord.Embed( - color=BLANK_COLOR, title="Command Logs", description="" - ) - - sorted_logs = sorted( - command_logs, key=lambda log: log.timestamp, reverse=True - ) - for log in sorted_logs: - if len(embed.description) > 3800: - break - embed.description += f"> [{log.username}](https://roblox.com/users/{log.user_id}/profile) ran the command `{log.command}` • \n" - - if embed.description in ["", "\n"]: - embed.description = "> No player logs found." - - embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - - if msg is None: - view = ReloadView( - self.bot, - ctx.author.id, - operate_and_reload_commandlogs, - [None, guild_id], - ) - msg = await ctx.send(embed=embed, view=view) - view.message = msg - view.callback_args[0] = msg - else: - await msg.edit(embed=embed) - - await operate_and_reload_commandlogs(None, guild_id) - - @mc.command(name="bans", description="Filter the bans of your server.") - @is_staff() - @is_mc_server_linked() - async def mc_bans( - self, - ctx: commands.Context, - username: typing.Optional[str], - user_id: typing.Optional[int], - ): - guild_id = ctx.guild.id - # status: ServerStatus = await self.bot.prc_api.get_server_status(guild_id) - try: - bans: list[prc_api.BanItem] = await self.bot.mc_api.fetch_bans(guild_id) - except prc_api.ResponseFailure: - return await ctx.send( - embed=discord.Embed( - title="MC API Error", - description="There were no bans, or your API key is incorrect.", - color=BLANK_COLOR, - ) - ) - embed = discord.Embed(color=BLANK_COLOR, title="Bans", description="") - status = username or user_id - - if not username and user_id: - username = "[PLACEHOLDER]" - - if not user_id and username: - user_id = "99999" - old_embed = copy.copy(embed) - embeds = [embed] - for log in bans: - if str(username or "") in str(log.username).lower() or str( - user_id or "" - ) in str(log.user_id): - embed = embeds[-1] - if len(embed.description) > 3800: - new = copy.copy(old_embed) - embeds.append(new) - embeds[ - -1 - ].description += f"> [{log.username}:{log.user_id}](https://roblox.com/users/{log.user_id}/profile)\n" - - if embeds[0].description in ["", "\n"]: - embeds[0].description = ( - "> This ban was not found." - if status - else "> Bans were not found in your server." - ) - - embeds[0].set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - - if len(embeds) > 1: - pages = [ - CustomPage(embeds=[embeds[i]], identifier=str(i + 1)) - for i in range(0, len(embeds) - 1) - ] - paginator = SelectPagination(self.bot, ctx.author.id, pages) - await ctx.send(embed=embeds[0], view=paginator.get_current_view()) - return - else: - await ctx.send(embed=embed) - - @mc.command(name="players", description="See all players in the server.") - @is_mc_server_linked() - async def mc_players( - self, ctx: commands.Context, filter: typing.Optional[str] = None - ): - guild_id = int(ctx.guild.id) - players: list[Player] = await self.bot.mc_api.get_server_players(guild_id) - embed2 = discord.Embed( - title=f"Server Players [{len(players)}]", color=BLANK_COLOR, description="" - ) - actual_players = [] - key_maps = {} - staff = [] - for item in players: - if item.permission == "Normal": - actual_players.append(item) - else: - staff.append(item) - - if filter not in [None, ""]: - actual_players_copy = [] - for item in actual_players: - if item.username.lower().startswith(filter.lower()): - actual_players_copy.append(item) - actual_players = actual_players_copy - staff_copy = [] - for item in staff: - if item.username.lower().startswith(filter.lower()): - staff_copy.append(item) - staff = staff_copy - - embed2.description += f"**Server Staff [{len(staff)}]**\n" + ( - ", ".join( - [ - f"[{plr.username}](https://roblox.com/users/{plr.id}/profile)" - for plr in staff - ] - ) - or "> No players in this category." - ) - - embed2.description += f"\n\n**Online Players [{len(actual_players)}]**\n" + ( - ", ".join( - [ - f"[{plr.username}](https://roblox.com/users/{plr.id}/profile)" - for plr in actual_players - ] - ) - or "> No players in this category." - ) - - embed2.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon) - if len(embed2.description) > 3999: - embed2.description = "" - embed2.description += f"**Server Staff [{len(staff)}]**\n" + ", ".join( - [f"{plr.username}" for plr in staff] - ) - - embed2.description += ( - f"\n\n**Online Players [{len(actual_players)}]**\n" - + ", ".join([f"{plr.username}" for plr in actual_players]) - ) - - await ctx.send(embed=embed2) - -async def setup(bot): - await bot.add_cog(MC(bot)) \ No newline at end of file diff --git a/datamodels/MapleKeys.py b/datamodels/MapleKeys.py deleted file mode 100644 index 569e60e5..00000000 --- a/datamodels/MapleKeys.py +++ /dev/null @@ -1,12 +0,0 @@ -from discord.ext import commands -import discord -from utils.mongo import Document -from datamodels.ServerKeys import ServerKey - - -class MapleKeys(Document): - async def get_server_key(self, guild_id: int): - doc = await self.db.find_one({"guildId": guild_id}) - if not doc: - return None - return ServerKey(guild_id=doc["guildId"], key=doc["uniqueToken"]) diff --git a/documentation/architecture.md b/documentation/architecture.md index ed42929d..c9d71c8e 100644 --- a/documentation/architecture.md +++ b/documentation/architecture.md @@ -49,7 +49,6 @@ They are loaded automatically on startup via `pkgutil.iter_modules`. | `GameLogging` | In-game event log forwarding to Discord channels | | `Infractions` | Staff infraction tracking and management | | `Jishaku` | Developer/debug REPL | -| `MC` | MapleCounty game integration | | `OAuth2` | Discord OAuth2 flow handling | | `Privacy` | User data and consent management | | `Punishments` | Player punishment logging (warns, bans, kicks, BOLOs) | @@ -106,7 +105,6 @@ staggered 2-second delay between each to avoid startup load spikes. | `iterate_conditions` | Periodic | Evaluates server conditions and triggers actions | | `check_infractions` | Hourly | Reverts expired temporary role changes from infractions | | `prc_automations` | Periodic | Runs configured PRC automation rules | -| `mc_discord_checks` | Periodic | Runs MapleCounty Discord integration checks | `check_reminders` and `iterate_conditions` are controlled by `REMINDERS_ENABLED` and `ACTIONS_ENABLED` respectively and will not @@ -139,7 +137,6 @@ Provides the `Document` base class used by all datamodels. Wraps |--------|----------| | `api.py` | FastAPI application exposing internal HTTP endpoints | | `prc_api.py` | Client for the ER:LC PRC API with typed response models | -| `mc_api.py` | Client for the MapleCounty API | | `utils.py` | General shared helpers used across cogs and tasks | | `autocompletes.py` | Discord slash command autocomplete handlers | | `conditions.py` | Condition evaluation logic used by the Actions system | diff --git a/documentation/database-schema.md b/documentation/database-schema.md index f2389e27..ee0a9985 100644 --- a/documentation/database-schema.md +++ b/documentation/database-schema.md @@ -303,17 +303,6 @@ Key fields: --- -### `maple_keys` - -MapleCounty integration API keys. - -Key fields: - -- `_id` — Authentication identifier -- `Key` — API key string - ---- - ### `prohibited_use_keys` Keys flagged for prohibited or abusive use. diff --git a/documentation/setup.md b/documentation/setup.md index 7c7ae045..a5372169 100644 --- a/documentation/setup.md +++ b/documentation/setup.md @@ -86,10 +86,6 @@ Required only for Activity Report and Duty Leaderboard spreadsheet exports: `CLIENT_X509_CERT_URL` - `DUTY_LEADERBOARD_ID` / `ACTIVITY_REPORT_ID` — Google Sheets document IDs -### Optional — MC API - -- `MC_API_URL` / `MC_API_KEY` — Used by the MapleCounty integration - ### Optional — Reserved - `GITHUB_TOKEN` — Reserved for future use, not currently used by the bot diff --git a/erm.py b/erm.py index 0523f53f..df34449e 100644 --- a/erm.py +++ b/erm.py @@ -8,14 +8,12 @@ from collections import defaultdict import asyncio -from datamodels.MapleKeys import MapleKeys from datamodels.Whitelabel import Whitelabel from utils.accounts import Accounts from utils.emojis import EmojiController from utils.log_tracker import LogTracker -from utils.mc_api import MCApiClient from utils.mongo import Document import aiohttp @@ -191,8 +189,6 @@ async def setup_hook(self) -> None: self.punishments = Warnings(self) self.settings = Settings(self.db, "settings") self.server_keys = ServerKeys(self.db, "server_keys") - self.maple_county = self.mongo[f"{f"{dbname}_" if dbname != "erm" else ""}MapleCounty"] - self.mc_keys = MapleKeys(self.maple_county, "Auth") self.sessions = Document(self.db, "sessions") self.staff_connections = StaffConnections(self.db, "staff_connections") @@ -215,9 +211,6 @@ async def setup_hook(self) -> None: ), api_key=config("PRC_API_KEY", default=None), ) - self.mc_api = MCApiClient( - self, base_url=config("MC_API_URL"), api_key=config("MC_API_KEY") - ) self.bloxlink = Bloxlink(self, config("BLOXLINK_API_KEY")) Extensions = [m.name for m in iter_modules(["cogs"], prefix="cogs.")] diff --git a/events/on_command_error.py b/events/on_command_error.py index adfcec27..377dd510 100644 --- a/events/on_command_error.py +++ b/events/on_command_error.py @@ -177,7 +177,6 @@ async def on_command_error(self, ctx, error): if isinstance(error, ServerLinkNotFound): aliases = { - "mc": "Maple County", "erlc": "ER:LC", } if error.code == 9999 and not do_not_send: diff --git a/tasks/iterate_conditions.py b/tasks/iterate_conditions.py index b9503bc2..68a35345 100644 --- a/tasks/iterate_conditions.py +++ b/tasks/iterate_conditions.py @@ -51,8 +51,6 @@ async def get_cached_guild(bot, guild_id): async def handle_erlc_condition(bot, guild_id, condition) -> bool: api_client = bot.prc_api - if await bot.mc_api.get_server_key(guild_id) is not None: - api_client = bot.mc_api try: players = await api_client.get_server_players(guild_id) except prc_api.ResponseFailure: diff --git a/tasks/mc_discord_checks.py b/tasks/mc_discord_checks.py deleted file mode 100644 index d3691500..00000000 --- a/tasks/mc_discord_checks.py +++ /dev/null @@ -1,198 +0,0 @@ -import re -import time -import discord -from discord.ext import tasks -import logging -import asyncio -from collections import defaultdict -import datetime -import pytz - -from utils.constants import BLANK_COLOR - - -_guild_cache = {} -_member_search_cache = defaultdict(dict) -_cache_timeout = 300 - -async def get_cached_member_by_username(bot, guild, username): - """Get member by username with caching""" - now = time.time() - cache_key = f"{guild.id}_{username.lower()}" - - if cache_key in _member_search_cache[guild.id]: - member_obj, cached_time = _member_search_cache[guild.id][cache_key] - if now - cached_time < _cache_timeout: - return member_obj - - member = await bot.accounts.roblox_to_discord(guild, username) - - _member_search_cache[guild.id][cache_key] = (member, now) - return member - -async def get_cached_guild(bot, guild_id): - """Get guild with caching""" - now = time.time() - cache_key = f"guild_{guild_id}" - - if cache_key in _guild_cache: - guild_obj, cached_time = _guild_cache[cache_key] - if now - cached_time < _cache_timeout and guild_obj: - return guild_obj - - guild = bot.get_guild(guild_id) - if not guild: - try: - guild = await bot.fetch_guild(guild_id) - except discord.NotFound: - guild = None - except discord.HTTPException: - guild = None - - _guild_cache[cache_key] = (guild, now) - return guild - - -async def get_cached_channel(bot, channel_id): - """Get channel with caching""" - now = time.time() - cache_key = f"channel_{channel_id}" - - if cache_key in _guild_cache: - channel_obj, cached_time = _guild_cache[cache_key] - if now - cached_time < _cache_timeout and channel_obj: - return channel_obj - - channel = bot.get_channel(channel_id) - if not channel: - try: - channel = await bot.fetch_channel(channel_id) - except discord.HTTPException: - channel = None - - _guild_cache[cache_key] = (channel, now) - return channel - - -@tasks.loop(minutes=10, reconnect=True) -async def mc_discord_checks(bot): - """ - Automated Discord Checks for MC Servers. - """ - initial_time = time.time() - - base = {"MC.discord_checks.enabled": True} - pipeline = [ - {"$match": base}, - { - "$lookup": { - "from": "mc_keys", - "localField": "_id", - "foreignField": "_id", - "as": "server_key", - } - }, - {"$match": {"server_key": {"$ne": []}}}, - ] - - semaphore = asyncio.Semaphore(3) - async def process_guild(items): - async with semaphore: - guild_id = items["_id"] - logging.info(f"Processing guild ID: {guild_id}") - - try: - settings = items["MC"].get("discord_checks", {}) - if not settings: - return - - if not settings.get("enabled", False): - return - - channel_id = settings.get("channel_id", 0) - channel = None - if channel_id != 0: - channel = await get_cached_channel(bot, channel_id) - if not channel: - logging.warning(f"Channel {channel_id} not found in guild {guild_id}.") - return - - guild = await get_cached_guild(bot, guild_id) - if not guild: - return - - try: - players = await bot.mc_api.get_server_players(guild_id) - if not players: - logging.info(f"No players found in guild {guild_id}") - return - - except Exception as e: - logging.warning(f"Failed to fetch server data for guild {guild_id}: {e}") - return - - not_in_discord = [] - for player in players: - member = await get_cached_member_by_username(bot, guild, player.username) - if not member: - not_in_discord.append(player) - - if not_in_discord: - await handle_discord_check_batch(bot, guild, not_in_discord, channel) - - except Exception as e: - logging.warning(f"Error processing guild {guild_id}: {e}", exc_info=True) - return - - guild_tasks = [] - async for items in await bot.settings.db.aggregate(pipeline): - guild_tasks.append(process_guild(items)) - - if len(guild_tasks) >= 5: - await asyncio.gather(*guild_tasks, return_exceptions=True) - guild_tasks = [] - await asyncio.sleep(2) - - if guild_tasks: - await asyncio.gather(*guild_tasks, return_exceptions=True) - - execution_time = time.time() - initial_time - logging.info(f"MC Discord Checks completed in {execution_time:.2f} seconds") - - -async def handle_discord_check_batch(bot, guild, players_not_in_discord, alert_channel): - """Handle batch of players not in Discord""" - if not players_not_in_discord: - return - - try: - if alert_channel: - await send_batch_warning_embed(players_not_in_discord, alert_channel) - - except Exception as e: - logging.warning(f"Error in handle_discord_check_batch: {e}") - - -async def send_batch_warning_embed(players, alert_channel): - """Send warning embed for multiple players""" - try: - player_list = [] - for player in players: - player_list.append(f"[{player.username}](https://roblox.com/users/{player.id}/profile)") - - embed = discord.Embed( - title="Maple County Discord Check Alert", - description=f""" - > The following players are currently in the server but not in the Discord server: - - {chr(10).join([f"> • {player}" for player in player_list])} - """, - color=BLANK_COLOR, - timestamp=datetime.datetime.now(tz=pytz.UTC), - ) - - await alert_channel.send(embed=embed) - except discord.HTTPException as e: - logging.warning(f"Failed to send batch embed: {e}") - except Exception as e: - logging.warning(f"Error in send_batch_warning_embed: {e}", exc_info=True) \ No newline at end of file diff --git a/ui/MapleCounty.py b/ui/MapleCounty.py deleted file mode 100644 index a7ae0388..00000000 --- a/ui/MapleCounty.py +++ /dev/null @@ -1,192 +0,0 @@ -from discord import Interaction -from discord.ext import commands -from utils.constants import blank_color -from utils.mc_api import ServerKey -from utils.utils import config_change_log -import discord - -class CustomModal(discord.ui.Modal, title="Edit Reason"): - def __init__(self, title, options, epher_args: dict = None): - super().__init__(title=title) - if epher_args is None: - epher_args = {} - self.saved_items = {} - self.epher_args = epher_args - self.interaction = None - - for name, option in options: - self.add_item(option) - self.saved_items[name] = option - - async def on_submit(self, interaction: discord.Interaction): - for key, item in self.saved_items.items(): - setattr(self, key, item) - self.interaction = interaction - await interaction.response.defer(**self.epher_args) - self.stop() - -class MapleCountyConfiguration(discord.ui.View): - def __init__(self, bot: commands.Bot, user_id: int, settings: dict = None): - super().__init__(timeout=None) - self.bot = bot - self.user_id = user_id - self.settings = settings or {} - - @discord.ui.button(label="Automatic Discord Checks", style=discord.ButtonStyle.secondary) - async def automatic_discord_checks(self, interaction: Interaction, button: discord.ui.Button): - sett = await self.bot.settings.find_by_id(interaction.guild.id) - if not sett: - sett = {"_id": interaction.guild.id} - - view = MCDiscordCheckConfig(self.bot, interaction.user.id, sett) - - discord_checks = sett.get('MC', {}).get('discord_checks', {}) - enabled = discord_checks.get('enabled', False) - channel_id = discord_checks.get('channel_id') - - embed = discord.Embed( - title="Automatic Discord Checks", - description=( - "> This module allows for automatic checks on Discord accounts of players in your Maple County server." - ), - color=blank_color, - ).add_field( - name="Alert Channel", - value=f"> Players that aren't in the Discord server will be sent in this channel.", - inline=False, - ) - - await interaction.response.send_message( - embed=embed, - view=view, - ephemeral=True, - ) - -class MCDiscordCheckConfig(discord.ui.View): - def __init__(self, bot: commands.Bot, user_id: int, settings: dict = None): - super().__init__(timeout=None) - self.bot = bot - self.user_id = user_id - self.settings = settings or {} - - # Add the select dropdown and channel select - self.add_item(self.create_select()) - self.add_item(self.create_channel_select()) - - def create_select(self): - select = discord.ui.Select( - placeholder="Enable/Disable Discord Checks", - options=[ - discord.SelectOption( - label="Enable Discord Checks", - value="enable", - default=self.settings.get('MC', {}).get('discord_checks', {}).get('enabled', False) - ), - discord.SelectOption( - label="Disable Discord Checks", - value="disable", - default=not self.settings.get('MC', {}).get('discord_checks', {}).get('enabled', False) - ), - ], - row=0 - ) - select.callback = self.select_callback - return select - - def create_channel_select(self): - current_channel_id = self.settings.get('MC', {}).get('discord_checks', {}).get('channel_id') - default_values = [discord.Object(id=current_channel_id)] if current_channel_id else None - - channel_select = discord.ui.ChannelSelect( - placeholder="Select Alert Channel", - channel_types=[discord.ChannelType.text], - max_values=1, - default_values=default_values, - row=1 - ) - channel_select.callback = self.channel_select_callback - return channel_select - - async def select_callback(self, interaction: Interaction): - if interaction.user.id != self.user_id: - await interaction.response.send_message( - "You are not permitted to interact with this dropdown.", - ephemeral=True - ) - return - - if interaction.data['values']: - selected_value = interaction.data['values'][0] - - sett = await self.bot.settings.find_by_id(interaction.guild.id) - if not sett: - sett = {"_id": interaction.guild.id} - - if "MC" not in sett: - sett["MC"] = {} - if "discord_checks" not in sett["MC"]: - sett["MC"]["discord_checks"] = {"enabled": False} - - if selected_value == "enable": - sett["MC"]["discord_checks"]["enabled"] = True - status = "enabled" - elif selected_value == "disable": - sett["MC"]["discord_checks"]["enabled"] = False - status = "disabled" - - await self.bot.settings.upsert(sett) - - await config_change_log( - self.bot, - interaction.guild, - interaction.user, - f"MC Discord Checks have been {status}." - ) - - await interaction.response.send_message( - f"Discord Checks have been {status}.", - ephemeral=True, - ) - else: - await interaction.response.send_message("No option selected.", ephemeral=True) - - async def channel_select_callback(self, interaction: Interaction): - if interaction.user.id != self.user_id: - await interaction.response.send_message( - "You are not permitted to interact with this dropdown.", - ephemeral=True - ) - return - - channel_select = None - for component in self.children: - if isinstance(component, discord.ui.ChannelSelect): - channel_select = component - break - - channel_id = channel_select.values[0].id if channel_select and channel_select.values else None - - sett = await self.bot.settings.find_by_id(interaction.guild.id) - if not sett: - sett = {"_id": interaction.guild.id} - - if "MC" not in sett: - sett["MC"] = {} - if "discord_checks" not in sett["MC"]: - sett["MC"]["discord_checks"] = {"enabled": False} - - sett["MC"]["discord_checks"]["channel_id"] = channel_id - - await self.bot.settings.upsert(sett) - - await config_change_log( - self.bot, - interaction.guild, - interaction.user, - f"MC Discord Checks alert channel set to <#{channel_id}>." - ) - - await interaction.response.send_message( - f"Alert channel set to <#{channel_id}>.", - ephemeral=True - ) \ No newline at end of file diff --git a/utils/conditions.py b/utils/conditions.py index 722e0490..45707982 100644 --- a/utils/conditions.py +++ b/utils/conditions.py @@ -111,7 +111,7 @@ def count_erlc_owners(players: list[Player]): def count_erlc_queue( queue: list[Player], -): # this one isnt supported for maple county yet +): return len(queue) @@ -191,12 +191,12 @@ def not_equals_to(v1: int, v2: int): } variable_table = [ - "ERLC_Players", # these obviously still work for maple county as well + "ERLC_Players", "ERLC_Moderators", "ERLC_Admins", "ERLC_Owner", "ERLC_Staff", - "ERLC_Queue", # this doesnt work for maple county :( + "ERLC_Queue", "ERLC_Police", "ERLC_Sheriff", "ERLC_Fire", diff --git a/utils/mc_api.py b/utils/mc_api.py deleted file mode 100644 index d23bd149..00000000 --- a/utils/mc_api.py +++ /dev/null @@ -1,182 +0,0 @@ -import asyncio -import typing -import aiohttp -from datamodels.ServerKeys import ServerKey -from utils.prc_api import ServerStatus, Player, CommandLog, BanItem -from utils.game_api_classes import ResponseFailure - -class MCApiClient: - def __init__(self, bot, base_url: str, api_key: str): - self.bot = bot - self.session = aiohttp.ClientSession() - self.api_key = api_key - self.base_url = base_url - - bot.external_http_sessions.append(self.session) - - async def get_server_key(self, guild_id: int) -> ServerKey: - return await self.bot.mc_keys.get_server_key(guild_id) - - async def _send_api_request( - self, - method: typing.Literal["GET", "POST"], - endpoint: str, - guild_id: int, - data: dict | None = None, - key: str | None = None, - ): - if not key: - internal_server_object = await self.get_server_key(guild_id) - internal_server_key = ( - internal_server_object if internal_server_object is not None else None - ) - if internal_server_key is None: - return 401, {} - else: - internal_server_key = internal_server_key.key - else: - internal_server_key = key - - async with self.session.request( - method, - url=f"{self.base_url}{endpoint}", - headers={ - "X-Static-Token": self.api_key, - "Authorization": internal_server_key, - "User-Agent": "ERM Bot (version 4)", - }, - json=data or {}, - ) as response: - if response.status == 429: - retry_after = int((await response.json()).get("retry_after", 5)) - await asyncio.sleep(retry_after) - return await self._send_api_request( - method=method, - endpoint=endpoint, - guild_id=guild_id, - data=data, - key=key, - ) - if response.status == 502: - return await self._send_api_request( - method=method, - endpoint=endpoint, - guild_id=guild_id, - data=data, - key=key, - ) - return response.status, ( - await response.json() if response.content_type != "text/html" else {} - ) - - async def get_server_status(self, guild_id: int): - status_code, response_json = await self._send_api_request( - "GET", "/Server", guild_id - ) - if status_code == 200: - return ServerStatus( - name=response_json["Name"], - owner_id=response_json["OwnerId"], - co_owner_ids=response_json["CoOwnerIds"], - current_players=response_json["CurrentPlayers"], - max_players=response_json["MaxPlayers"], - join_key=response_json["JoinKey"], - # account_verified_request=response_json['AccVerifiedReq'] == 'Enabled', - # team_balance=response_json['TeamBalance'] - ) - else: - raise ResponseFailure(status_code=status_code, json_data=response_json) - - async def send_test_request(self, server_key: str) -> int | ServerStatus: - code, response_json = await self._send_api_request( - "GET", "/Server", 0, None, server_key - ) - return ( - code - if code != 200 - else ServerStatus( - name=response_json["Name"], - owner_id=response_json["OwnerId"], - co_owner_ids=response_json["CoOwnerIds"], - current_players=response_json["CurrentPlayers"], - max_players=response_json["MaxPlayers"], - join_key=response_json["JoinKey"], - # account_verified_request=response_json['AccVerifiedReq'] == 'Enabled', - noah forgot to implement this! - # team_balance=response_json['TeamBalance'] - ) - ) - - async def get_server_players(self, guild_id: int) -> list: - status_code, response_json = await self._send_api_request( - "GET", "/Server/Players", guild_id - ) - if status_code == 200: - new_list = [] - for item in response_json: - new_list.append( - Player( - username=item["Player"].split(":")[0], - id=item["Player"].split(":")[1], - permission=item["Permission"], - callsign=item.get("Callsign"), - team=item["Team"], - ) - ) - return new_list - else: - raise ResponseFailure(status_code=status_code, json_data=response_json) - - async def authorize(self, roblox_id: int, server_name: str, guild_id: int): - status_code, response_json = await self._send_api_request( - "POST", - "/Server/Auth", - 0, - {"RobloxId": roblox_id, "ServerName": server_name, "GuildId": guild_id}, - key="__PRE_AUTHORIZATION", - ) - - if status_code == 200: - return response_json["token"] - else: - raise ResponseFailure(status_code=status_code, json_data=response_json) - - async def fetch_server_logs(self, guild_id: int): - status_code, response_json = await self._send_api_request( - "GET", "/Server/Commands", guild_id - ) - if status_code == 200: - return [ - CommandLog( - username=( - log_item["Player"].split(":")[0] - if ":" in log_item["Player"] - else log_item["Player"] - ), - user_id=( - log_item["Player"].split(":")[1] - if ":" in log_item["Player"] - else 0 - ), - timestamp=log_item["Timestamp"], - is_automated=log_item["Player"] == "Remote Server", - command=log_item["Command"], - ) - for log_item in response_json - ] - else: - raise ResponseFailure(status_code=status_code, json_data=response_json) - - async def fetch_bans(self, guild_id: int): - status_code, response_json = await self._send_api_request( - "GET", "/Server/Bans", guild_id - ) - - if status_code == 200: - if response_json == []: - return [] - return [ - BanItem(user_id=int(user_id), username=username) - for user_id, username in response_json.items() - ] - else: - raise ResponseFailure(status_code=status_code, json_data=response_json)