Skip to content

Commit

Permalink
Merge pull request #2026 from avrae/AVR-419
Browse files Browse the repository at this point in the history
Avr 419
  • Loading branch information
SeanStoves committed Apr 9, 2024
2 parents 1cd4ec2 + 9c5b781 commit 9f403f7
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 20 deletions.
4 changes: 3 additions & 1 deletion cogs5e/initiative/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from utils.functions import smart_trim
from .types import CombatantType

import ldclient

if TYPE_CHECKING:
from utils.context import AvraeContext
from . import Combatant, CombatantGroup, Combat
Expand Down Expand Up @@ -46,7 +48,7 @@ async def nlp_feature_flag_enabled(bot):
return await bot.ldclient.variation(
"cog.initiative.upenn_nlp.enabled",
# since NLP recording is keyed on the server ID, we just use a throwaway key
{"key": "anonymous", "anonymous": True},
ldclient.Context.create("anonymous"),
default=False,
)

Expand Down
39 changes: 35 additions & 4 deletions cogs5e/sheetManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,11 @@ async def character_server(self, ctx, *, name: str = None):
and new_character_to_set.upstream == server_character.upstream
and server_character.is_active_server(ctx)
):
message = f"'{server_character.name}' is already the server character. Use the `!char server reset` command if you want to no longer use a server character here."
message = (
f"'{server_character.name}' is already the server character. "
f"Use the `!char server reset` command if you want to no longer "
f"use a server character here."
)
embed = await self._active_character_embed(ctx, message)
await ctx.send(embed=embed, delete_after=DELETE_AFTER_SECONDS)
return
Expand Down Expand Up @@ -544,7 +548,11 @@ async def character_channel(self, ctx, *, name: str = None):
and new_character_to_set.upstream == channel_character.upstream
and channel_character.is_active_channel(ctx)
):
message = f"'{channel_character.name}' is already the channel character. Use the `!char channel reset` command if you want to no longer use a channel character here."
message = (
f"'{channel_character.name}' is already the channel character. "
f"Use the `!char channel reset` command if you want to no "
f"longer use a channel character here."
)
embed = await self._active_character_embed(ctx, message)
await ctx.send(embed=embed, delete_after=DELETE_AFTER_SECONDS)
return
Expand Down Expand Up @@ -611,7 +619,18 @@ async def reset_all(self, ctx):

@character.command(name="list")
async def character_list(self, ctx):
"""Lists your characters."""
"""
Lists the characters owned by the user.
This command retrieves all the characters owned by the user from the database, and sends an embed containing
the names of these characters. If the user has an active character, it is highlighted in the embed.
Args:
ctx (Context): The context in which the command was called.
Returns:
None
"""
user_characters = await self.bot.mdb.characters.find(
{"owner": str(ctx.author.id)}, ["name", "upstream"]
).to_list(None)
Expand Down Expand Up @@ -645,7 +664,19 @@ async def character_list(self, ctx):

@character.command(name="delete")
async def character_delete(self, ctx, *, name):
"""Deletes a character."""
"""
Deletes a character.
This command deletes a character from the user's character list. The user is asked to confirm the deletion before
the character is deleted. If the user has no characters, a message is sent to the user and the command ends.
Args:
ctx (Context): The context in which the command was called.
name (str): The name of the character to delete.
Returns:
None
"""
user_characters = await self.bot.mdb.characters.find(
{"owner": str(ctx.author.id)}, ["name", "upstream"]
).to_list(None)
Expand Down
11 changes: 10 additions & 1 deletion ddb/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,17 @@ def to_dict(self):
}

def to_ld_dict(self):
"""Returns a dict representing the DDB user in LaunchDarkly."""
"""
Returns a dictionary representing the DDB user in LaunchDarkly.
This method is used to convert the user's information into a format that can be used by LaunchDarkly.
The returned dictionary includes the user's ID, username, roles, subscription status, and subscription tier.
Returns:
dict: A dictionary containing the user's information in a format compatible with LaunchDarkly.
"""
return {
"kind": "user",
"key": self.user_id,
"name": self.username,
"custom": {
Expand Down
6 changes: 3 additions & 3 deletions gamedata/compendium.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ async def load_all_mongodb(self, mdb):

ldclient.set_config(ldclient.Config(sdk_key=config.LAUNCHDARKLY_SDK_KEY))

if ldclient.get().variation(
"data.monsters.gridfs", {"key": "anonymous-user-start-bot", "anonymous": True}, False
):
# TODO: Try importing Context as a standalone method
context = ldclient.Context.create("anonymous-user-start-bot")
if ldclient.get().variation("data.monsters.gridfs", context, False):
fs = motor.motor_asyncio.AsyncIOMotorGridFSBucket(mdb)
data = await fs.open_download_stream_by_name(filename="monsters")
gridout = await data.read()
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ aiobotocore==2.1.0
redis==4.6.0
cachetools==4.2.2
httplib2==0.19.0
launchdarkly-server-sdk==7.2.0
markdownify==0.9.4
motor==2.3.1
psutil==5.8.0
Expand All @@ -19,6 +18,8 @@ pyyaml==6.0.1
sentry-sdk==1.3.0

# Already Updated Packages
# Top Level Deps
launchdarkly-server-sdk==9.0
Pillow~=10.2.0
disnake~=2.9.1
gspread~=6.0.0
Expand Down
63 changes: 53 additions & 10 deletions utils/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Optional, TYPE_CHECKING

import ldclient
from ldclient.config import Config

if TYPE_CHECKING:
import ddb.auth
Expand All @@ -15,26 +16,68 @@ class AsyncLaunchDarklyClient(ldclient.LDClient):
"""Works exactly like a normal LDClient, except certain blocking methods run in a separate thread."""

def __init__(self, loop, sdk_key, *args, **kwargs):
config = ldclient.Config(sdk_key=sdk_key, *args, **kwargs)
super().__init__(config=config)
super().__init__(
config=Config(sdk_key=sdk_key)
) # Required for SDK 8.0 and above. *args, **kwargs are not supported
self.loop = loop

async def variation(self, key, user, default): # run variation evaluation in a separate thread
return await self.loop.run_in_executor(None, super().variation, key, user, default)

async def variation_for_discord_user(self, key: str, user: "disnake.User", default):
"""Return a variation for a key given a discord user."""
return await self.variation(key, discord_user_to_dict(user), default)
"""
Returns a variation for a given key based on a Discord user.
This method is used to determine the variation for a feature flag key based on the Discord user's information.
The user's information is converted into a format compatible with LaunchDarkly using the discord_user_to_context method.
Args:
key (str): The feature flag key for which the variation is to be determined.
user ("disnake.User"): The Discord user based on whose information the variation is to be determined.
default: The default value to return if the feature flag key is not found.
Returns:
The variation for the given feature flag key based on the Discord user's information.
"""
return await self.variation(key, discord_user_to_context(user), default)

async def variation_for_ddb_user(self, key: str, user: Optional["ddb.auth.BeyondUser"], default, discord_id: int):
"""Return a variation for a key given a DDB user or None."""
"""
Returns a variation for a given key based on a DDB user or None.
This method is used to determine the variation for a feature flag key based on the user's information.
If the user is None, an anonymous context is created using the discord_id.
If the user is a DDB user, the user's information is converted into a format compatible with LaunchDarkly using the to_ld_dict method.
Args:
key (str): The feature flag key for which the variation is to be determined.
user (Optional["ddb.auth.BeyondUser"]): The DDB user based on whose information the variation is to be determined. If None, an anonymous context is created.
default: The default value to return if the feature flag key is not found.
discord_id (int): The discord_id of the user. Used to create an anonymous context if user is None.
Returns:
The variation for the given feature flag key based on the user's information.
"""
if user is None:
user = {"key": str(discord_id), "anonymous": True}
# TODO: Check if second argument is valid for "anonymous" as a -kind- of user
user = ldclient.Context.create(str(discord_id))
else:
user = user.to_ld_dict()
user = ldclient.Context.from_dict(user.to_ld_dict())
return await self.variation(key, user, default)


def discord_user_to_dict(user):
"""Converts a Discord user to a user dict for LD."""
return {"key": str(user.id), "name": str(user)}
# Updated from discord_user_to_dict to discord_user_to_context
def discord_user_to_context(user):
"""
Converts a Discord user to a context for LaunchDarkly.
This function takes a Discord user as input and converts it into a context that can be used by LaunchDarkly.
The user's ID is used as the key, and the user's name is used as the name in the context.
Args:
user: The Discord user to be converted into a LaunchDarkly context.
Returns:
A LaunchDarkly context created from the Discord user's information.
"""
return ldclient.Context.builder(str(user.id)).name(str(user)).build()

0 comments on commit 9f403f7

Please sign in to comment.