Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cogs/ERLC.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,7 @@ async def operate_and_reload_serverinfo(
queue: int = await self.bot.prc_api.get_server_queue(
guild_id, minimal=True
) # this only returns the count
client = roblox.Client()
client = self.bot.roblox

embed1 = discord.Embed(title=f"{status.name}", color=BLANK_COLOR)
embed1.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon)
Expand All @@ -881,10 +881,14 @@ async def operate_and_reload_serverinfo(
),
inline=False,
)
try:
owner_name = (await client.get_user(status.owner_id)).name
except:
owner_name = "Unknown"
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"> **Owner:** [{owner_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,
Expand Down
397 changes: 397 additions & 0 deletions cogs/Sessions.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion erm.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ async def setup_hook(self) -> None:
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")
self.ics = IntegrationCommandStorage(self.db, "logged_command_data")
self.actions = Actions(self.db, "actions")
Expand Down
44 changes: 44 additions & 0 deletions tasks/check_sessions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from discord.ext import tasks
import discord
import logging
from erm import Bot
import discord.http
import json
@tasks.loop(minutes=5, reconnect=True)
async def check_sessions(bot: Bot):
print("Ran!")
async for session in bot.sessions.db.find({"dynamic": True}):
print(f"Parsing session for guild {session["_id"]}")
try:
guild = session["_id"]
g = await bot.fetch_guild(guild)
settings = await bot.settings.find(guild)
try:
info = await bot.prc_api.get_server_status(guild)
except: info = None
print(info)
d = settings["sessions"]["start"].replace(
"{user}",
session["user"]
).replace(
"{erlc.name}",
info.name if info else "{erlc.name}"
).replace(
"{user_mentions}",
f"{" | ".join([f"<@{user}>" for user in session["voted_users"]])}"
).replace(
"{erlc.code}",
f"{info.join_key}" if info else "{erlc.code}"
).replace(
"{erlc.players}",
str(info.current_players) if info else "{erlc.players}"
)
j = json.loads(d)
await bot.http.edit_message(settings["sessions"]["channel_id"], session["message"], params=discord.http.MultipartParameters(payload = j, multipart=None, files=None))
if info:
if info.current_players > session["analytics"]["max_players"]:
session["analytics"]["max_players"] = info.current_players
session["analytics"]["player_counts"].append(info.current_players)
await bot.sessions.update(session)
except Exception as e:
logging.warning(f"error: {str(e)}")
103 changes: 103 additions & 0 deletions ui/CustomModals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import discord, typing
from menus import CustomModal
from utils.utils import generalised_interaction_check_failure

class CustomModalButton(discord.ui.Button):
def __init__(
self,
user_id,
title: str,
label: str,
options: typing.List[typing.Tuple[str | typing.Literal[str], discord.ui.TextInput | discord.ui.Label | discord.ui.TextDisplay]],
epher_args: typing.Optional[dict] = None,
):
super().__init__(label=label or "Enter Strike Amount", style=discord.ButtonStyle.secondary)
self.value = None
self.user_id = user_id
self.modal: typing.Union[None, CustomModal] = None
self.title = title or self.label
self.label = label
self.options = options
self.epher_args = epher_args or {}

# 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.
async def callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.defer(ephemeral=True, thinking=True)
return await generalised_interaction_check_failure(interaction.followup)

self.modal = CustomModal(self.label, self.options, self.epher_args)
await interaction.response.send_modal(self.modal)
await self.modal.wait()

self.values = []
for component in self.modal.children:
if isinstance(component, discord.ui.TextDisplay): continue
if isinstance(component, discord.ui.Label):
# Labels have a .component attribute
target = component.component
else:
# Other components are the target themselves
target = component

# Skip if target doesn't have value/values
if hasattr(target, 'values'):
self.values.append(target.values)
elif hasattr(target, 'value'):
self.values.append(target.value)
self.parent.view.values = self.values
self.parent.view.stop()
return

class CustomModalExecutorButton(discord.ui.Button):
def __init__(
self,
user_id,
title: str,
label: str,
options: typing.List[typing.Tuple[str | typing.Literal[str], discord.ui.TextInput | discord.ui.Label]],
func: typing.Callable,
epher_args: typing.Optional[dict] = None,

):
super().__init__(label=label or "Enter Strike Amount", style=discord.ButtonStyle.secondary)
self.value = None
self.user_id = user_id
self.modal: typing.Union[None, CustomModal] = None
self.title = title or self.label
self.label = label
self.options = options
self.func = func
self.epher_args = epher_args or {}

# 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.
async def callback(self, interaction: discord.Interaction):
if interaction.user.id != self.user_id:
await interaction.response.defer(ephemeral=True, thinking=True)
return await generalised_interaction_check_failure(interaction.followup)

self.modal = CustomModal(self.label, self.options, self.epher_args)
await interaction.response.send_modal(self.modal)
await self.modal.wait()

self.values = []
for component in self.modal.children:
if isinstance(component, discord.ui.TextDisplay): continue
if isinstance(component, discord.ui.Label):
# Labels have a .component attribute
target = component.component
else:
# Other components are the target themselves
target = component

# Skip if target doesn't have value/values
if hasattr(target, 'values'):
self.values.append(target.values)
elif hasattr(target, 'value'):
self.values.append(target.value)
await self.func(interaction, self)

8 changes: 8 additions & 0 deletions ui/Selects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import discord
class SimpleTextChannelSelect(discord.ui.ChannelSelect):
def __init__(self, limit=1, **kwargs):
super().__init__(placeholder="Select Channels" if limit > 1 else "Select a Channel", max_values=limit, channel_types=[discord.ChannelType.text], **kwargs)

async def callback(self, interaction: discord.Interaction):
await interaction.response.defer()
await self.parent.view.stop()
149 changes: 149 additions & 0 deletions ui/Sessions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import discord
from discord.ext import commands
import typing
from menus import CustomModal
from base64 import urlsafe_b64decode, b64encode
import json


class SessionsEmbedCreationView(discord.ui.LayoutView):
def __init__(self, bot: commands.Bot, type: typing.Literal['vote', 'start', 'shutdown'],):
super().__init__(timeout=None)
self.cont = discord.ui.Container()
self.type = type
self.bot = bot
match type:
case 'vote':
self.cont.add_item(
discord.ui.TextDisplay(
"### Create Session Vote Message\n"
"Please use Discohook to design your session vote message. When you are done, please press the button saying 'I Have Created My Message'. Please note that you may only have one message.\n"
"**Main Variables**\n"
"- Your vote button must be a button and must have the label set to `{vote_button}`. If it is not set to that, your vote button will not work.\n"
"- If you want a view votes button, you must have another button with the label set to `{view_votes_button}`, else, that will not work.\n"
"**Other Variables**\n"
"- {user}: The user initiating the session vote\n"
"- {vote_button_name}: The name of the vote button. If you select the dynamic button option (where the button's name changes on the amount of votes), this will say 'vote' by default.\n"
"- {required_members}: How many members are needed to start the vote\n"
)
)
case 'start':
self.cont.add_item(discord.ui.TextDisplay(
"### Create Session Start Message\n"
"Please use Discohook to create your session start message. When you are done, press the button saying 'I Have Created My Message'. Please note that you may only have one message.\n"
"**Main Variables and Notes**\n"
"- You will not be able to add any other components than __link buttons__. Regular buttons and anything else will be ignored.\n"
"- It is advised that if you create a join URL, you set the code part to {erlc.code}. It will automatically change to your join code.\n"
"**Other Variables**\n"
"- {user}: The user who started the session\n"
"- {user_mentions}: The mentions of the users who voted.\n"
"- {erlc}: A variable with references to your linked ERLC server\n"
" - {erlc.name}: The name of your ER:LC server\n"
" - {erlc.code}: The code to your ER:LC server\n"
" - {erlc.players}: The players in your ER:LC server. If this is present, your message will be edited every 5 minutes to reflect the current amount of members.\n"
))
case 'shutdown':
self.cont.add_item(discord.ui.TextDisplay(
"### Create Session End Message\n"
"Please use Discohook to create your session end message. When you are done, press the button saying 'I Have Created My Message'. Please note that you may only have one message.\n"
"**Main Variables and Notes**\n"
"- Only link buttons are permitted.\n"
"**Other Variables**:\n"
"- {user}: The user responsible for ending this session\n"
"- {erlc}: A variable with references to your linked ERLC server \n"
" - {erlc.name}: The name of your ER:LC server\n"
" - {erlc.max_players}: The highest amount of players."
))
self.cont.add_item(discord.ui.Separator())
self.row = discord.ui.ActionRow(discord.ui.Button(url="https://discohook.app", label="Access Discohook"))
self.button = discord.ui.Button(
label = "I Have Created My Message",
style=discord.ButtonStyle.blurple
)
self.button.callback = self.submit
self.row.add_item(self.button)
self.cont.add_item(self.row)
self.add_item(self.cont)
async def submit(self, interaction: discord.Interaction):
try:
settings = await self.bot.settings.find(interaction.guild.id)
if not settings.get('sessions'):
return
modal = CustomModal(
"Submit Message",
[
(
"url",
discord.ui.Label(
text="URL",
description="Please send your URL into this textbox.",
component=discord.ui.TextInput(max_length=3999)
)
)
]
)
await interaction.response.send_modal(modal)
await modal.wait()
val: str = modal.url.component.value
if not val.startswith("https://discohook.app"):
return await interaction.followup.send("This is not a valid discohook.app URL.", ephemeral=True)
try:
data_encoded = val.split("?data=")[1]
except:
return await interaction.followup.send("This discohook.app url does not have a message attached.")
print(data_encoded)

data = urlsafe_b64decode(data_encoded + "==").decode()
try:
data = json.loads(data)
except:
return await interaction.followup.send("The data is not valid. Please try again.")

message = data["messages"][0]["data"]
self.satisfied_conditions = False # Checks for buttons being correct.
if not message["components"] and self.type != "vote":
print(message["components"])
self.satisfied_conditions = True
else:
for component in message["components"][0]["components"]: #
print(component)
if not component["type"] == 2: # button
return await interaction.followup.send("Your data has components that are not permitted.")
match self.type:
case 'vote':
if component["label"] == "{vote_button}":
if not settings["sessions"].get("dynamic_button", True):
component["label"] = settings["sessions"]["vote_button_label"]
if component["style"] == 5: continue
component["custom_id"] = f"vote_button:{interaction.guild.id}"
self.satisfied_conditions = True
continue
if component["label"] == "{view_votes_button}":
if component["style"] == 5: continue
component["label"] = "View Votes"
component["custom_id"] = f"view_votes_button:{interaction.guild.id}"
continue
if component["style"] == 5:
if component["custom_id"]:
del component["custom_id"]
case 'start':
if component["style"] == 5:
if component["custom_id"]:
del component["custom_id"]
self.satisfied_conditions = True
break

case 'shutdown':
if component["style"] == 5:
self.satisfied_conditions = True
break

if not self.satisfied_conditions:
return await interaction.followup.send("Your components are invalid for the specific type of embed you are making. This may be because you have regular buttons in the start and end styling (which only allow links) or you may have missing buttons in the vote area.")

settings["sessions"][self.type] = json.dumps(message)
print(settings["sessions"])
await self.bot.settings.update(settings)
self.stop()
except Exception as e:
print(str(e.with_traceback(None)))
5 changes: 4 additions & 1 deletion utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,7 @@
},
"remove_ingame_perms": False,
"end_shift": False
}
}

CUSTOM_IDS_FOR_SESSIONS = ["vote_button", "view_votes_button"]
SESSION_VIEW_TYPES = ["vote", "start", "shutdown"]
6 changes: 3 additions & 3 deletions utils/mongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, connection: AsyncDatabase, document_name):
self.logger = logging.getLogger(__name__)

# <-- Pointer Methods -->
async def update(self, dict):
async def update(self, dict) -> None:
"""
For simpler calls, points to self.update_by_id
"""
Expand All @@ -46,7 +46,7 @@ async def delete(self, id):
await self.delete_by_id(id)

# <-- Actual Methods -->
async def find_by_id(self, id):
async def find_by_id(self, id) -> dict | None:
"""
Returns the data found under `id`
Params:
Expand Down Expand Up @@ -149,7 +149,7 @@ async def increment(self, id, amount, field):
"""
await self.db.update_one({"_id": id}, {"$inc": {field: amount}})

async def get_all(self):
async def get_all(self) -> list[dict]:
"""
Returns a list of all data in the document
"""
Expand Down
2 changes: 1 addition & 1 deletion utils/prc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ async def send_test_request(self, server_key: str) -> int | ServerStatus:
)
)

async def get_server_players(self, guild_id: int) -> list:
async def get_server_players(self, guild_id: int) -> list[Player]:
status_code, response_json = await self._send_api_request(
"GET", "/server/players", guild_id
)
Expand Down
Loading
Loading