Skip to content

Base Setting

All configuration types in GigaJoyce extend from the same abstract base: Setting.

This base class defines the metadata (name, description, id, type, locales, module_name) and the contract that each interactive setting must implement.

class Setting(Generic[T]):
    """
    Base class for interactive bot settings.

    Args:
        name (str): Display name for the setting.
        description (str): Description shown in embeds and menus.
        id (str): Unique identifier key for persistence.
        type_ (str): String discriminator for the type.
        locales (bool): Whether i18n/locales should apply.
        module_name (Optional[str]): Associated module, if any.
    """

    async def run(self, view: InteractionView) -> T:
        """Execute the interactive editor inside Discord."""

    def parse_to_database(self, value: T) -> Any:
        """Serialize value to a database-friendly representation."""

    def parse_from_database(self, config: Any) -> T:
        """Reconstruct value from stored configuration."""

    def parse_to_field(self, value: T, translator: Optional[callable] = None) -> str:
        """Render a human-readable field string (for embeds)."""

    def clone(self) -> "Setting":
        """Return a safe copy of the setting instance."""

Responsibilities

  • Store metadata about the setting.
  • Define the common contract (run, parse_*, clone).
  • Ensure consistency for all subclasses (e.g., numbers, strings, roles).

Behavior

When a guild or user edits configurations, the bot instantiates these settings and runs them inside an InteractionView. Each subclass defines its own editor but keeps this shared interface.

settings.Setting

Setting

Bases: ABC, Generic[T]

Abstract base class for all settings.

A Setting[T] represents a configurable value owned either by a Guild or a Member (user). Subclasses define how the value is: - run (collected/edited via UI), - serialized to database (parse_to_database), - deserialized from database (parse_from_database / parse).

Attributes:

Name Type Description
name

Human‑readable name to display in UIs.

description

Short explanation of what this setting controls.

id

Unique identifier for persistence (used as the document field key).

type

String type label (e.g., "string", "boolean", "role", etc.).

value

Current in‑memory value (deserialized form).

permission

Optional permission bit/level required to change this setting.

locales

Whether this setting’s display strings should be translated.

module_name

Owning module name, if the setting belongs to a module.

kwargs

Extra configuration specific to a given concrete setting type.

Source code in settings/Setting.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
class Setting(ABC, Generic[T]):
    """Abstract base class for all settings.

    A `Setting[T]` represents a configurable value owned either by a **Guild**
    or a **Member** (user). Subclasses define how the value is:
    - **run** (collected/edited via UI),
    - **serialized** to database (**parse_to_database**),
    - **deserialized** from database (**parse_from_database** / **parse**).

    Attributes:
        name: Human‑readable name to display in UIs.
        description: Short explanation of what this setting controls.
        id: Unique identifier for persistence (used as the document field key).
        type: String type label (e.g., "string", "boolean", "role", etc.).
        value: Current in‑memory value (deserialized form).
        permission: Optional permission bit/level required to change this setting.
        locales: Whether this setting’s display strings should be translated.
        module_name: Owning module name, if the setting belongs to a module.
        kwargs: Extra configuration specific to a given concrete setting type.
    """

    def __init__(
        self,
        name: str,
        description: str,
        id: str,
        type_: str,
        value: Optional[T] = None,
        permission: Optional[int] = None,
        locales: Optional[bool] = False,
        module_name: Optional[str] = None,
        **kwargs,
    ):
        self.name = name
        self.description = description
        self.id = id
        self.type = type_
        self.value = value
        self.permission = permission
        self.locales = locales
        self.module_name = module_name
        self.kwargs = kwargs


    def run(self, view: InteractionView) -> Awaitable[T]:
        """Execute the interactive flow for this setting.

        Implementations should render the necessary UI (buttons, selects, etc.)
        and resolve with the updated value.

        Args:
            view: Active `InteractionView` used to present the UI.

        Returns:
            An awaitable that resolves to the new value of type `T`.

        Raises:
            NotImplementedError: If the subclass does not override this method.
        """
        raise NotImplementedError("Must be implemented in derived classes.")


    def parse_to_database(self, value: T) -> Any:
        """Serialize the runtime value into a DB‑friendly representation.

        This is the inverse of `parse_from_database`.

        Args:
            value: The runtime value.

        Returns:
            A JSON‑serializable value suitable for your database.

        Raises:
            NotImplementedError: If the subclass does not override this method.
        """
        raise NotImplementedError("Must be implemented in derived classes.")


    def parse_from_database(self, config: Any) -> T:
        """Reconstruct the runtime value from the DB representation.

        This is the inverse of `parse_to_database`.

        Args:
            config: Raw stored value from the database.

        Returns:
            The deserialized runtime value of type `T`.

        Raises:
            NotImplementedError: If the subclass does not override this method.
        """
        raise NotImplementedError("Must be implemented in derived classes.")

    async def parse(self, config: Any, client: ExtendedClient, guild_data: Any, guild: Guild) -> Awaitable[T]:
        """Optional async hook to parse a value with context.

        Subclasses may override this to perform context‑aware parsing (e.g.
        resolve IDs to Discord objects). Default behavior returns `config` as‑is.

        Args:
            config: Raw config value from DB or input.
            client: Extended bot client.
            guild_data: Raw guild document (for extra context if needed).
            guild: Hydrated guild wrapper.

        Returns:
            The parsed value (usually of type `T`).
        """
        return config

    async def save(self, client: ExtendedClient, entity: Union["Guild", "Member"], setting: "Setting[T]") -> bool:
        """Persist the setting value using the client DB API.

        Default behavior:
          1. Serializes `setting.value` via `parse_to_database` if available.
          2. Writes to:
             - `guilds` collection with filter `{"_id": str(guild.id)}` when
               `entity` is a `Guild`;
             - `members` collection with filter `{"_id": str(member.id)}` when
               `entity` is a `Member`.

        Note:
            If your schema uses different keys (e.g., composite keys
            `{"id": ..., "guildId": ...}` for members), override this method in
            your concrete setting type or adapt it at call‑site.

        Args:
            client: Extended bot client (exposes `client.db.update_one`).
            entity: Guild or Member instance that owns the setting.
            setting: The concrete `Setting` instance being saved.

        Returns:
            An awaitable that resolves truthy on success.
        """
        client.logger.debug(f"Using default save method for setting: {self.id}")

        if hasattr(setting, "parse_to_database") and callable(setting.parse_to_database):
            if setting.value is not None:
                value = setting.parse_to_database(setting.value)
            else:
                client.logger.warning(f"Setting value is None; storing as None in database.")
                value = None
        else:
            client.logger.warning(f"Setting does not have a parse_to_database method. Using raw value.")
            value = setting.value

        query = {f"settings.{setting.id}": value}

        if isinstance(entity, Guild):
            result = await client.db.update_one(
                "guilds",
                {"_id": str(entity.id)},
                {"$set": query},
            )
            return bool(result)
        elif isinstance(entity, Member):
            result = await client.db.update_one(
                "members",
                {"_id": str(entity.id)},
                {"$set": query},
            )
            return bool(result)
        else:
            raise TypeError("Entity must be a Guild or Member.")

    def apply_locale(self, translate_module: Callable[[str], str], clone: Optional[bool] = False) -> Union["Setting[T]", tuple[str, str, str]]:
        """Apply module‑scoped translation to all display fields.

        If `self.locales` is truthy, this will translate `name`, `description`,
        and any string values inside `kwargs` using the provided function.

        Args:
            translate_module: Function that maps a translation key to its value.
            clone: If True, returns a **new** translated copy of this setting;
                otherwise returns a tuple `(name, description, kwargs)` with
                translated values without mutating the instance.

        Returns:
            - If `clone=True`: a new `Setting` instance (same type) with fields
              translated;
            - If `clone=False`: a tuple `(name, description, kwargs)`; or
            - If `self.locales` is falsy: `None`.
        """
        if self.locales:
            if clone:
                setting_clone = self.clone()
                setting_clone.name = translate_module(self.name)
                setting_clone.description = translate_module(self.description)
                setting_clone.kwargs = {}
                for key, value in self.kwargs.items():
                    if isinstance(value, str):
                        setting_clone.kwargs[key] = translate_module(value)
                return setting_clone
            else:
                name = translate_module(self.name)
                description = translate_module(self.description)
                kwargs = {}
                for key, value in self.kwargs.items():
                    kwargs[key] = translate_module(value)
                return name, description, kwargs

    def propagate_locales(self, child: "Setting[Any]"):
        """Propagate `locales` and `module_name` flags to a child setting.

        Useful when composite settings are composed of other settings and you
        want consistent translation behavior across the hierarchy.

        Args:
            child: The setting that should inherit locale info.
        """
        if self.locales and self.module_name:
            child.locales = self.locales
            child.module_name = self.module_name

    def clone(self) -> "Setting[T]":
        """Return a shallow copy of this setting instance.

        The copy is constructed by re‑invoking the concrete class `__init__`
        with parameters that exist as attributes on `self`.

        Returns:
            A new instance of the concrete `Setting` subclass.
        """
        cls = self.__class__
        init_params = cls.__init__.__code__.co_varnames[1:]  # Ignora 'self'
        init_args = {key: getattr(self, key) for key in init_params if hasattr(self, key)}
        return cls(**init_args)

apply_locale(translate_module, clone=False)

Apply module‑scoped translation to all display fields.

If self.locales is truthy, this will translate name, description, and any string values inside kwargs using the provided function.

Parameters:

Name Type Description Default
translate_module Callable[[str], str]

Function that maps a translation key to its value.

required
clone Optional[bool]

If True, returns a new translated copy of this setting; otherwise returns a tuple (name, description, kwargs) with translated values without mutating the instance.

False

Returns:

Type Description
Union[Setting[T], tuple[str, str, str]]
  • If clone=True: a new Setting instance (same type) with fields translated;
Union[Setting[T], tuple[str, str, str]]
  • If clone=False: a tuple (name, description, kwargs); or
Union[Setting[T], tuple[str, str, str]]
  • If self.locales is falsy: None.
Source code in settings/Setting.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def apply_locale(self, translate_module: Callable[[str], str], clone: Optional[bool] = False) -> Union["Setting[T]", tuple[str, str, str]]:
    """Apply module‑scoped translation to all display fields.

    If `self.locales` is truthy, this will translate `name`, `description`,
    and any string values inside `kwargs` using the provided function.

    Args:
        translate_module: Function that maps a translation key to its value.
        clone: If True, returns a **new** translated copy of this setting;
            otherwise returns a tuple `(name, description, kwargs)` with
            translated values without mutating the instance.

    Returns:
        - If `clone=True`: a new `Setting` instance (same type) with fields
          translated;
        - If `clone=False`: a tuple `(name, description, kwargs)`; or
        - If `self.locales` is falsy: `None`.
    """
    if self.locales:
        if clone:
            setting_clone = self.clone()
            setting_clone.name = translate_module(self.name)
            setting_clone.description = translate_module(self.description)
            setting_clone.kwargs = {}
            for key, value in self.kwargs.items():
                if isinstance(value, str):
                    setting_clone.kwargs[key] = translate_module(value)
            return setting_clone
        else:
            name = translate_module(self.name)
            description = translate_module(self.description)
            kwargs = {}
            for key, value in self.kwargs.items():
                kwargs[key] = translate_module(value)
            return name, description, kwargs

clone()

Return a shallow copy of this setting instance.

The copy is constructed by re‑invoking the concrete class __init__ with parameters that exist as attributes on self.

Returns:

Type Description
Setting[T]

A new instance of the concrete Setting subclass.

Source code in settings/Setting.py
230
231
232
233
234
235
236
237
238
239
240
241
242
def clone(self) -> "Setting[T]":
    """Return a shallow copy of this setting instance.

    The copy is constructed by re‑invoking the concrete class `__init__`
    with parameters that exist as attributes on `self`.

    Returns:
        A new instance of the concrete `Setting` subclass.
    """
    cls = self.__class__
    init_params = cls.__init__.__code__.co_varnames[1:]  # Ignora 'self'
    init_args = {key: getattr(self, key) for key in init_params if hasattr(self, key)}
    return cls(**init_args)

parse(config, client, guild_data, guild) async

Optional async hook to parse a value with context.

Subclasses may override this to perform context‑aware parsing (e.g. resolve IDs to Discord objects). Default behavior returns config as‑is.

Parameters:

Name Type Description Default
config Any

Raw config value from DB or input.

required
client ExtendedClient

Extended bot client.

required
guild_data Any

Raw guild document (for extra context if needed).

required
guild Guild

Hydrated guild wrapper.

required

Returns:

Type Description
Awaitable[T]

The parsed value (usually of type T).

Source code in settings/Setting.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
async def parse(self, config: Any, client: ExtendedClient, guild_data: Any, guild: Guild) -> Awaitable[T]:
    """Optional async hook to parse a value with context.

    Subclasses may override this to perform context‑aware parsing (e.g.
    resolve IDs to Discord objects). Default behavior returns `config` as‑is.

    Args:
        config: Raw config value from DB or input.
        client: Extended bot client.
        guild_data: Raw guild document (for extra context if needed).
        guild: Hydrated guild wrapper.

    Returns:
        The parsed value (usually of type `T`).
    """
    return config

parse_from_database(config)

Reconstruct the runtime value from the DB representation.

This is the inverse of parse_to_database.

Parameters:

Name Type Description Default
config Any

Raw stored value from the database.

required

Returns:

Type Description
T

The deserialized runtime value of type T.

Raises:

Type Description
NotImplementedError

If the subclass does not override this method.

Source code in settings/Setting.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def parse_from_database(self, config: Any) -> T:
    """Reconstruct the runtime value from the DB representation.

    This is the inverse of `parse_to_database`.

    Args:
        config: Raw stored value from the database.

    Returns:
        The deserialized runtime value of type `T`.

    Raises:
        NotImplementedError: If the subclass does not override this method.
    """
    raise NotImplementedError("Must be implemented in derived classes.")

parse_to_database(value)

Serialize the runtime value into a DB‑friendly representation.

This is the inverse of parse_from_database.

Parameters:

Name Type Description Default
value T

The runtime value.

required

Returns:

Type Description
Any

A JSON‑serializable value suitable for your database.

Raises:

Type Description
NotImplementedError

If the subclass does not override this method.

Source code in settings/Setting.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def parse_to_database(self, value: T) -> Any:
    """Serialize the runtime value into a DB‑friendly representation.

    This is the inverse of `parse_from_database`.

    Args:
        value: The runtime value.

    Returns:
        A JSON‑serializable value suitable for your database.

    Raises:
        NotImplementedError: If the subclass does not override this method.
    """
    raise NotImplementedError("Must be implemented in derived classes.")

propagate_locales(child)

Propagate locales and module_name flags to a child setting.

Useful when composite settings are composed of other settings and you want consistent translation behavior across the hierarchy.

Parameters:

Name Type Description Default
child Setting[Any]

The setting that should inherit locale info.

required
Source code in settings/Setting.py
217
218
219
220
221
222
223
224
225
226
227
228
def propagate_locales(self, child: "Setting[Any]"):
    """Propagate `locales` and `module_name` flags to a child setting.

    Useful when composite settings are composed of other settings and you
    want consistent translation behavior across the hierarchy.

    Args:
        child: The setting that should inherit locale info.
    """
    if self.locales and self.module_name:
        child.locales = self.locales
        child.module_name = self.module_name

run(view)

Execute the interactive flow for this setting.

Implementations should render the necessary UI (buttons, selects, etc.) and resolve with the updated value.

Parameters:

Name Type Description Default
view InteractionView

Active InteractionView used to present the UI.

required

Returns:

Type Description
Awaitable[T]

An awaitable that resolves to the new value of type T.

Raises:

Type Description
NotImplementedError

If the subclass does not override this method.

Source code in settings/Setting.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def run(self, view: InteractionView) -> Awaitable[T]:
    """Execute the interactive flow for this setting.

    Implementations should render the necessary UI (buttons, selects, etc.)
    and resolve with the updated value.

    Args:
        view: Active `InteractionView` used to present the UI.

    Returns:
        An awaitable that resolves to the new value of type `T`.

    Raises:
        NotImplementedError: If the subclass does not override this method.
    """
    raise NotImplementedError("Must be implemented in derived classes.")

save(client, entity, setting) async

Persist the setting value using the client DB API.

Default behavior
  1. Serializes setting.value via parse_to_database if available.
  2. Writes to:
  3. guilds collection with filter {"_id": str(guild.id)} when entity is a Guild;
  4. members collection with filter {"_id": str(member.id)} when entity is a Member.
Note

If your schema uses different keys (e.g., composite keys {"id": ..., "guildId": ...} for members), override this method in your concrete setting type or adapt it at call‑site.

Parameters:

Name Type Description Default
client ExtendedClient

Extended bot client (exposes client.db.update_one).

required
entity Union[Guild, Member]

Guild or Member instance that owns the setting.

required
setting Setting[T]

The concrete Setting instance being saved.

required

Returns:

Type Description
bool

An awaitable that resolves truthy on success.

Source code in settings/Setting.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
async def save(self, client: ExtendedClient, entity: Union["Guild", "Member"], setting: "Setting[T]") -> bool:
    """Persist the setting value using the client DB API.

    Default behavior:
      1. Serializes `setting.value` via `parse_to_database` if available.
      2. Writes to:
         - `guilds` collection with filter `{"_id": str(guild.id)}` when
           `entity` is a `Guild`;
         - `members` collection with filter `{"_id": str(member.id)}` when
           `entity` is a `Member`.

    Note:
        If your schema uses different keys (e.g., composite keys
        `{"id": ..., "guildId": ...}` for members), override this method in
        your concrete setting type or adapt it at call‑site.

    Args:
        client: Extended bot client (exposes `client.db.update_one`).
        entity: Guild or Member instance that owns the setting.
        setting: The concrete `Setting` instance being saved.

    Returns:
        An awaitable that resolves truthy on success.
    """
    client.logger.debug(f"Using default save method for setting: {self.id}")

    if hasattr(setting, "parse_to_database") and callable(setting.parse_to_database):
        if setting.value is not None:
            value = setting.parse_to_database(setting.value)
        else:
            client.logger.warning(f"Setting value is None; storing as None in database.")
            value = None
    else:
        client.logger.warning(f"Setting does not have a parse_to_database method. Using raw value.")
        value = setting.value

    query = {f"settings.{setting.id}": value}

    if isinstance(entity, Guild):
        result = await client.db.update_one(
            "guilds",
            {"_id": str(entity.id)},
            {"$set": query},
        )
        return bool(result)
    elif isinstance(entity, Member):
        result = await client.db.update_one(
            "members",
            {"_id": str(entity.id)},
            {"$set": query},
        )
        return bool(result)
    else:
        raise TypeError("Entity must be a Guild or Member.")