Edit on GitHub

tickets_plus.database.layer

A layer for the database session.

This module contains the OnlineConfig class. It is used to make the database session easier to use. It is also an async context manager. Generally, you should use this class instead of the session directly. Additionally, of note is the fact that one-on-one relationships are loaded by default. But any relationship that is a one-to-many or many-to-many relationship is not loaded by default. This is due to efficiency concerns. If you need to load a relationship, you can use the options argument, which is a list of sqlalchemy.sql.base.ExecutableOptions. For more information on ExecutableOptions, see the SQLAlchemy documentation.

Typical usage example:
async with OnlineConfig(bot, session) as db:
    guild = await db.get_guild(guild_id)
    # Do something with the guild
    # and if you changed something
    await db.commit()
  1"""A layer for the database session.
  2
  3This module contains the `OnlineConfig` class.
  4It is used to make the database session easier to use.
  5It is also an async context manager.
  6Generally, you should use this class instead of the session directly.
  7Additionally, of note is the fact that one-on-one relationships are loaded
  8by default. But any relationship that is a one-to-many or many-to-many
  9relationship is not loaded by default.
 10This is due to efficiency concerns.
 11If you need to load a relationship, you can use the options argument, which
 12is a list of `sqlalchemy.sql.base.ExecutableOption`s.
 13For more information on `ExecutableOption`s, see the SQLAlchemy documentation.
 14
 15Typical usage example:
 16    ```py
 17    async with OnlineConfig(bot, session) as db:
 18        guild = await db.get_guild(guild_id)
 19        # Do something with the guild
 20        # and if you changed something
 21        await db.commit()
 22    ```
 23"""
 24# License: EPL-2.0
 25# SPDX-License-Identifier: EPL-2.0
 26# Copyright (c) 2021-present The Tickets+ Contributors
 27# This Source Code may also be made available under the following
 28# Secondary Licenses when the conditions for such availability set forth
 29# in the Eclipse Public License, v. 2.0 are satisfied: GPL-3.0-only OR
 30# If later approved by the Initial Contributor, GPL-3.0-or-later.
 31
 32import datetime
 33import types
 34from typing import Any, Sequence, Tuple, Type
 35
 36import discord
 37from discord import utils
 38from discord.ext import commands
 39from sqlalchemy import sql
 40from sqlalchemy.ext import asyncio as sa_asyncio
 41from sqlalchemy.sql import base
 42
 43from tickets_plus.database import models
 44
 45
 46class OnlineConfig:
 47    """A convenience layer for the database session.
 48
 49    This class is used to make the database session easier to use.
 50    It also handles the 'async with' statement.
 51    Any and all commits have to be done manually.
 52    """
 53
 54    def __init__(self, bot_instance: commands.AutoShardedBot, session: sa_asyncio.AsyncSession) -> None:
 55        """Initialises the database session layer.
 56
 57        Wraps the provided session in an async context manager.
 58        And set the bot instance as a private attribute.
 59
 60        Args:
 61            bot_instance: The bot instance.
 62            session: The database session.
 63        """
 64        self._session = session
 65        self._bot = bot_instance
 66
 67    async def __aenter__(self) -> "OnlineConfig":
 68        """Enter the 'async with' statement.
 69
 70        This method is called when entering the 'async with' statement.
 71
 72        Returns:
 73            OnlineConfig: The database session layer.
 74        """
 75        return self
 76
 77    async def __aexit__(self, exc_type: Type[BaseException] | None, exc_value: BaseException | None,
 78                        traceback: types.TracebackType | None) -> None:
 79        """Exit the 'async with' statement.
 80
 81        This method is called when exiting the 'async with' statement.
 82        Or when an exception is raised.
 83        We roll back the session if an exception is raised.
 84        We then close the session regardless.
 85
 86        Args:
 87            exc_type: The exception type.
 88            exc_value: The exception value.
 89            traceback: The traceback.
 90        """
 91        if exc_type:
 92            await self.rollback()
 93        await self.close()
 94
 95    async def close(self) -> None:
 96        """Close the database session.
 97
 98        This method closes the underlying SQLAlchemy session.
 99        """
100        await self._session.close()
101
102    async def flush(self) -> None:
103        """Flush the database session.
104
105        Moves all the changes to the database transaction.
106        """
107        await self._session.flush()
108
109    async def commit(self) -> None:
110        """Commit the database session.
111
112        Commits the underlying SQLAlchemy session.
113        """
114        await self._session.commit()
115
116    async def rollback(self) -> None:
117        """Rollback the database session.
118
119        Cleans all the non-committed changes.
120        This includes the flushed changes.
121        """
122        await self._session.rollback()
123
124    async def delete(self, obj) -> None:
125        """Delete a row from the database.
126
127        Marks the object for deletion.
128        The deletion will be done on the next flush.
129        """
130        await self._session.delete(obj)
131
132    async def get_guild(self, guild_id: int, options: Sequence[base.ExecutableOption] | None = None) -> models.Guild:
133        """Get or create a guild from the database.
134
135        Fetches a guild from the database.
136        Due to the convenience of the database layer,
137        we guarantee a guild will always be returned.
138        It will be created if it does not exist.
139        However, we do not commit the changes.
140
141        Args:
142            guild_id: The guild ID.
143            options: The options to use when querying the database.
144                Those options are mostly used for relationship loading.
145                As in async, we can't use lazy loading.
146
147        Returns:
148            models.Guild: The guild.
149                With the relationships loaded if options are provided.
150                Otherwise, attempting to access the relationships will
151                result in an error.
152        """
153        if options:
154            guild_conf = await self._session.scalar(
155                sql.select(models.Guild).where(models.Guild.guild_id == guild_id).options(*options))
156        else:
157            guild_conf = await self._session.scalar(sql.select(models.Guild).where(models.Guild.guild_id == guild_id))
158        if guild_conf is None:
159            guild_conf = models.Guild(guild_id=guild_id)
160            self._session.add(guild_conf)
161        return guild_conf
162
163    async def get_user(self, user_id: int, options: Sequence[base.ExecutableOption] | None = None) -> models.User:
164        """Get or create a user from the database.
165
166        Fetches a user from the database.
167        Due to the convenience of the database layer,
168        we guarantee a user will always be returned.
169        It will be created if it does not exist.
170        However, we do not commit the changes.
171
172        Args:
173            user_id: The user ID.
174            options: The options to use when querying the database.
175                Those options are mostly used for relationship loading.
176                As in async, we can't use lazy loading.
177
178        Returns:
179            models.User: The user.
180                With the relationships loaded if options are provided.
181                Otherwise, attempting to access the relationships will
182                result in an error.
183        """
184        if options:
185            user = await self._session.scalar(
186                sql.select(models.User).where(models.User.user_id == user_id).options(*options))
187        else:
188            user = await self._session.scalar(sql.select(models.User).where(models.User.user_id == user_id))
189        if user is None:
190            user = models.User(user_id=user_id)
191            self._session.add(user)
192        return user
193
194    async def get_member(self, user_id: int, guild_id: int) -> models.Member:
195        """Get or create a member from the database.
196
197        Fetches a member from the database.
198        If the member does not exist, it will be created.
199        We also check if the guild exists and create it if it does not.
200        We also check if the user exists and create it if it does not.
201
202        Args:
203            user_id: The user ID.
204            guild_id: The guild ID.
205
206        Returns:
207            models.Member: The member.
208                As members have a one-to-one relationship with guilds,
209                and a one-to-one relationship with users, we automatically
210                load the guild and user relationships.
211        """
212        guild = await self.get_guild(guild_id)
213        user = await self.get_user(user_id)
214        member_conf = await self._session.scalar(
215            sql.select(models.Member).where(models.Member.user == user, models.Member.guild == guild))
216        if member_conf is None:
217            member_conf = models.Member(user=user, guild=guild)
218            self._session.add(member_conf)
219        return member_conf
220
221    async def get_expired_members(self) -> Sequence[models.Member]:
222        """Get expired members from the database.
223
224        Fetches all members with expired status from the database.
225        Used to clean up the roles.
226
227        Returns:
228            Sequence[models.Member]: The members with expired status.
229        """
230        time = datetime.datetime.utcnow()
231        expr_members = await self._session.scalars(sql.select(models.Member).where(models.Member.status_till <= time))
232        return expr_members.all()
233
234    async def get_ticket_bot(self, user_id: int, guild_id: int) -> Tuple[bool, models.TicketBot]:
235        """Get or create a ticket bot from the database.
236
237        Fetches a ticket bot from the database.
238        If the ticket bot does not exist, it will be created.
239        We also check if the guild exists and create it if it does not.
240
241        Args:
242            user_id: The user ID.
243            guild_id: The guild ID.
244
245        Returns:
246            Tuple[bool, models.TicketBot]: A tuple containing a boolean
247                indicating if the ticket bot was created, and the ticket bot.
248                Relationships are loaded.
249        """
250        guild = await self.get_guild(guild_id)
251        ticket_user = await self._session.scalar(
252            sql.select(models.TicketBot).where(models.TicketBot.user_id == user_id, models.TicketBot.guild == guild))
253        new = False
254        if ticket_user is None:
255            new = True
256            ticket_user = models.TicketBot(user_id=user_id, guild=guild)
257            self._session.add(ticket_user)
258        return new, ticket_user
259
260    async def check_ticket_bot(self, user_id: int, guild_id: int) -> bool:
261        """Check if the ticket user exists.
262
263        A more efficient way to check if a ticket user exists.
264        Instead of attempting to create the ticket user,
265        we just check if it exists.
266        This is the only check that uses a guild ID.
267        This is because the user ID is shared across all guilds.
268
269        Args:
270            user_id: The user ID.
271            guild_id: The guild ID.
272
273        Returns:
274            bool: A boolean indicating if the ticket user exists.
275        """
276        ticket_user = await self._session.scalar(
277            sql.select(models.TicketBot).where(models.TicketBot.user_id == user_id,
278                                               models.TicketBot.guild_id == guild_id))
279        return ticket_user is not None
280
281    async def get_ticket_type(self,
282                              guild_id: int,
283                              name: str,
284                              comping: bool = False,
285                              comaccs: bool = False,
286                              strpbuttns: bool = False,
287                              ignore: bool = False) -> Tuple[bool, models.TicketType]:
288        """Get or create a ticket type from the database.
289
290        Fetches a ticket type from the database.
291        If the ticket type does not exist, it will be created.
292        We also check if the guild exists and create it if it does not.
293
294        Args:
295            guild_id: The guild ID.
296            name: The ticket type name.
297            comping: The comping flag.
298            comaccs: The comaccs flag.
299            strpbuttns: The strpbuttns flag.
300            ignore: The ignore flag.
301
302        Returns:
303            Tuple[bool, models.TicketType]: A tuple containing a boolean
304                indicating if the ticket type was created, and the ticket type.
305                Relationships are loaded.
306        """
307        guild = await self.get_guild(guild_id)
308        ticket_type = await self._session.scalar(
309            sql.select(models.TicketType).where(models.TicketType.guild == guild, models.TicketType.prefix == name))
310        new = False
311        if ticket_type is None:
312            new = True
313            ticket_type = models.TicketType(guild=guild,
314                                            prefix=name,
315                                            comping=comping,
316                                            comaccs=comaccs,
317                                            strpbuttns=strpbuttns,
318                                            ignore=ignore)
319            self._session.add(ticket_type)
320        return new, ticket_type
321
322    async def get_ticket_types(self, guild_id: int) -> Sequence[models.TicketType]:
323        """Get ticket types from the database.
324
325        Fetches all ticket types from the database.
326        We also check if the guild exists and create it if it does not.
327
328        Args:
329            guild_id: The guild ID.
330
331        Returns:
332            Sequence[models.TicketType]: The ticket types.
333        """
334        guild = await self.get_guild(guild_id)
335        ticket_types = await self._session.scalars(
336            sql.select(models.TicketType).where(models.TicketType.guild == guild))
337        return ticket_types.all()
338
339    async def fetch_ticket(self, channel_id: int) -> models.Ticket | None:
340        """Fetch a ticket from the database.
341
342        Attempts to fetch a ticket from the database.
343        If the ticket does not exist, None is returned.
344        If you want to create a ticket if it does not exist,
345        use get_ticket instead.
346
347        Args:
348            channel_id: The channel ID.
349
350        Returns:
351            models.Ticket | None: The ticket.
352        """
353        ticket = await self._session.get(models.Ticket, channel_id)
354        return ticket
355
356    async def get_ticket(self,
357                         channel_id: int,
358                         guild_id: int,
359                         user_id: int | None = None,
360                         staff_note: int | None = None) -> Tuple[bool, models.Ticket]:
361        """Get or create a ticket from the database.
362
363        Fetches a ticket from the database.
364        If the ticket does not exist, it will be created.
365        We also check if the guild exists and create it if it does not.
366        If you want to check if a ticket exists, use fetch_ticket instead.
367
368        Args:
369            channel_id: The channel ID.
370            guild_id: The guild ID. Used to create the guild if missing.
371            user_id: The user ID. Only filed if integration is enabled.
372            staff_note: The staff note thread ID. Used to annotate the thread,
373                if the ticket is created.
374
375        Returns:
376            Tuple[bool, models.Ticket]: A tuple containing a boolean
377                indicating if the ticket was created, and the ticket.
378                Relationships are loaded.
379        """
380        guild = await self.get_guild(guild_id)
381        ticket = await self._session.get(models.Ticket, channel_id)
382        new = False
383        if ticket is None:
384            new = True
385            ticket = models.Ticket(channel_id=channel_id, guild=guild, user_id=user_id, staff_note_thread=staff_note)
386            self._session.add(ticket)
387        return new, ticket
388
389    async def get_pending_tickets(self) -> Sequence[models.Ticket]:
390        """Get pending tickets from the database.
391
392        Fetches all pending tickets from the database.
393
394        Returns:
395            Sequence[models.Ticket]: The pending tickets.
396        """
397        tickets = await self._session.scalars(
398            sql.select(models.Ticket).join(models.Guild).filter(
399                models.Guild.warn_autoclose.isnot(None), models.Ticket.notified.isnot(True), models.Ticket.last_response
400                <= models.UTCnow() - models.Guild.warn_autoclose))
401        return tickets.all()
402
403    async def fetch_tag(self, guild_id: int, tag: str) -> discord.Embed | str | None:
404        """Fetch a tag from the database.
405
406        Attempts to fetch a tag from the database.
407        If the tag does not exist, None is returned.
408        If you want to create a tag if it does not exist,
409        use get_tag instead.
410
411        Args:
412            guild_id: The guild ID.
413            tag: The tag.
414
415        Returns:
416            discord.Embed | str | None: The tag.
417        """
418        guild = await self.get_guild(guild_id)
419        embed = await self._session.scalar(
420            sql.select(models.Tag).where(models.Tag.guild == guild, models.Tag.tag_name == tag))
421        if embed is None:
422            return None
423        if embed.title:
424            emdd = vars(embed)
425            emd2 = {}
426            for key, data in emdd.items():
427                if data is not None:
428                    emd2[key] = data
429            if embed.author:
430                emd2["author"] = {"name": embed.author}
431            if embed.footer:
432                emd2["footer"] = {"text": embed.footer}
433            result = discord.Embed.from_dict(emd2)
434            result.timestamp = utils.utcnow()
435            return result
436        return embed.description
437
438    async def get_tag(
439        self,
440        guild_id: int,
441        tag_name: str,
442        description: str,
443        embed_args: dict[str, Any] | None = None,
444    ) -> Tuple[bool, models.Tag]:
445        """Get or create a tag from the database.
446
447        Fetches a tag from the database.
448        If the tag does not exist, it will be created.
449        We also check if the guild exists and create it if it does not.
450        If you want to check if a tag exists, use fetch_tag instead.
451
452        Args:
453            guild_id: The guild ID.
454            tag_name: The tag.
455            description: The description.
456            embed_args: The embed arguments.
457                Basically, the arguments to pass to discord.Embed,
458                when using discord.Embed.from_dict.
459        """
460        guild = await self.get_guild(guild_id)
461        tag = await self._session.scalar(
462            sql.select(models.Tag).where(models.Tag.guild == guild, models.Tag.tag_name == tag_name))
463        new = False
464        if tag is None:
465            new = True
466            if embed_args is None:
467                embed_args = {}
468            tag = models.Tag(guild=guild, tag_name=tag_name, description=description, **embed_args)
469            self._session.add(tag)
470        return new, tag
471
472    async def get_tags(self, guild_id: int) -> Sequence[models.Tag]:
473        """Get tags from the database.
474
475        Fetches all tags from the database.
476        We also check if the guild exists and create it if it does not.
477
478        Args:
479            guild_id: The guild ID.
480
481        Returns:
482            Sequence[models.Tag]: The tags.
483        """
484        guild = await self.get_guild(guild_id)
485        tags = await self._session.scalars(sql.select(models.Tag).where(models.Tag.guild == guild))
486        return tags.all()
487
488    async def get_staff_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.StaffRole]:
489        """Get or create the staff role from the database.
490
491        Fetches a staff role from the database.
492        If the staff role does not exist, it will be created.
493        We also check if the guild exists and create it if it does not.
494
495        Args:
496            role_id: The role ID.
497            guild_id: The guild ID.
498
499        Returns:
500            Tuple[bool, models.StaffRole]: A tuple containing a boolean
501                indicating if the staff role was created, and the staff role.
502                Relationships are loaded.
503        """
504        guild = await self.get_guild(guild_id)
505        staff_role = await self._session.get(models.StaffRole, role_id)
506        new = False
507        if staff_role is None:
508            new = True
509            staff_role = models.StaffRole(role_id=role_id, guild=guild)
510            self._session.add(staff_role)
511        return new, staff_role
512
513    async def get_all_staff_roles(self, guild_id: int) -> Sequence[models.StaffRole]:
514        """Get all staff roles from the database.
515
516        Fetches all staff roles from the database.
517        If the staff roles do not exist, an empty list is returned.
518
519        Args:
520            guild_id: The guild ID.
521
522        Returns:
523            Sequence[models.StaffRole]: A list of staff roles.
524                Relationships are loaded.
525        """
526        guild = await self.get_guild(guild_id)
527        staff_roles = await self._session.scalars(sql.select(models.StaffRole).where(models.StaffRole.guild == guild))
528        return staff_roles.all()
529
530    async def check_staff_role(self, role_id: int) -> bool:
531        """Check if the staff role exists.
532
533        A more efficient way to check if a staff role exists.
534        Instead of attempting to create the staff role,
535        we just check if it exists.
536
537        Args:
538            role_id: The role ID.
539
540        Returns:
541            bool: A boolean indicating if the staff role exists.
542        """
543        staff_role = await self._session.get(models.StaffRole, role_id)
544        return staff_role is not None
545
546    async def get_observers_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.ObserversRole]:
547        """Get or create the observer role from the database.
548
549        Fetches an observer role from the database.
550        If the observer role does not exist, it will be created.
551        We also check if the guild exists and create it if it does not.
552
553        Args:
554            role_id: The role ID.
555            guild_id: The guild ID.
556
557        Returns:
558            Tuple[bool, models.ObserversRole]: A tuple containing a boolean
559                indicating if the observer role was created, and the observers'
560                role. Relationships are loaded.
561        """
562        guild = await self.get_guild(guild_id)
563        observers_role = await self._session.get(models.ObserversRole, role_id)
564        new = False
565        if observers_role is None:
566            new = True
567            observers_role = models.ObserversRole(role_id=role_id, guild=guild)
568            self._session.add(observers_role)
569        return new, observers_role
570
571    async def get_all_observers_roles(self, guild_id: int) -> Sequence[models.ObserversRole]:
572        """Get all observer roles from the database.
573
574        Fetches all observer roles from the database.
575        If the observer roles do not exist, an empty list is returned.
576
577        Args:
578            guild_id: The guild ID.
579
580        Returns:
581            Sequence[models.ObserversRole]: A list of observer roles.
582                Relationships are loaded.
583        """
584        guild = await self.get_guild(guild_id)
585        observers_roles = await self._session.scalars(
586            sql.select(models.ObserversRole).where(models.ObserversRole.guild == guild))
587        return observers_roles.all()
588
589    async def check_observers_role(self, role_id: int) -> bool:
590        """Check if the observer role exists.
591
592        A more efficient way to check if an observer role exists.
593        Instead of attempting to create the observer role,
594        we just check if it exists.
595
596        Args:
597            role_id: The role ID.
598
599        Returns:
600            bool: A boolean indicating if the observer role exists.
601        """
602        observers_role = await self._session.get(models.ObserversRole, role_id)
603        return observers_role is not None
604
605    async def get_community_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.CommunityRole]:
606        """Get or create the community role from the database.
607
608        Fetches a community role from the database.
609        If the community role does not exist, it will be created.
610        We also check if the guild exists and create it if it does not.
611
612        Args:
613            role_id: The role ID.
614            guild_id: The guild ID.
615
616        Returns:
617            Tuple[bool, models.CommunityRole]: A tuple containing a boolean
618                indicating if the community role was created, and the community
619                role. Relationships are loaded.
620        """
621        guild = await self.get_guild(guild_id)
622        community_role = await self._session.get(models.CommunityRole, role_id)
623        new = False
624        if community_role is None:
625            new = True
626            community_role = models.CommunityRole(role_id=role_id, guild=guild)
627            self._session.add(community_role)
628        return new, community_role
629
630    async def get_all_community_roles(self, guild_id: int) -> Sequence[models.CommunityRole]:
631        """Get all community roles from the database.
632
633        Fetches all community roles from the database.
634        If the community roles do not exist, an empty list is returned.
635
636        Args:
637            guild_id: The guild ID.
638
639        Returns:
640            Sequence[models.CommunityRole]: A list of community roles.
641                Relationships are loaded.
642        """
643        guild = await self.get_guild(guild_id)
644        community_roles = await self._session.scalars(
645            sql.select(models.CommunityRole).where(models.CommunityRole.guild == guild))
646        return community_roles.all()
647
648    async def check_community_role(self, role_id: int) -> bool:
649        """Check if the community role exists.
650
651        A more efficient way to check if a community role exists.
652        Instead of attempting to create the community role,
653        we just check if it exists.
654
655        Args:
656            role_id: The role ID.
657
658        Returns:
659            bool: A boolean indicating if the community role exists.
660        """
661        community_role = await self._session.get(models.CommunityRole, role_id)
662        return community_role is not None
663
664    async def get_community_ping(self, role_id: int, guild_id: int) -> Tuple[bool, models.CommunityPing]:
665        """Get or create the community ping from the database.
666
667        Fetches a community ping from the database.
668        If the community ping does not exist, it will be created.
669        We also check if the guild exists and create it if it does not.
670
671        Args:
672            role_id: The role ID.
673            guild_id: The guild ID.
674
675        Returns:
676            Tuple[bool, models.CommunityPing]: A tuple containing a boolean
677                indicating if the community ping was created, and the community
678                pings. Relationships are loaded.
679        """
680        guild = await self.get_guild(guild_id)
681        community_ping = await self._session.get(models.CommunityPing, role_id)
682        new = False
683        if community_ping is None:
684            new = True
685            community_ping = models.CommunityPing(role_id=role_id, guild=guild)
686            self._session.add(community_ping)
687        return new, community_ping
688
689    async def get_all_community_pings(self, guild_id: int) -> Sequence[models.CommunityPing]:
690        """Get all community pings from the database.
691
692        Fetches all community pings from the database.
693        If the community pings do not exist, an empty list is returned.
694
695        Args:
696            guild_id: The guild ID.
697
698        Returns:
699            Sequence[models.CommunityPing]: A list of community pings.
700                Relationships are loaded.
701        """
702        guild = await self.get_guild(guild_id)
703        community_pings = await self._session.scalars(
704            sql.select(models.CommunityPing).where(models.CommunityPing.guild == guild))
705        return community_pings.all()
706
707    async def check_community_ping(self, role_id: int) -> bool:
708        """Check if the community ping exists.
709
710        A more efficient way to check if a community ping exists.
711        Instead of attempting to create the community ping,
712        we just check if it exists.
713
714        Args:
715            role_id: The role ID.
716
717        Returns:
718            bool: A boolean indicating if the community ping exists.
719        """
720        community_ping = await self._session.get(models.CommunityPing, role_id)
721        return community_ping is not None
class OnlineConfig:
 47class OnlineConfig:
 48    """A convenience layer for the database session.
 49
 50    This class is used to make the database session easier to use.
 51    It also handles the 'async with' statement.
 52    Any and all commits have to be done manually.
 53    """
 54
 55    def __init__(self, bot_instance: commands.AutoShardedBot, session: sa_asyncio.AsyncSession) -> None:
 56        """Initialises the database session layer.
 57
 58        Wraps the provided session in an async context manager.
 59        And set the bot instance as a private attribute.
 60
 61        Args:
 62            bot_instance: The bot instance.
 63            session: The database session.
 64        """
 65        self._session = session
 66        self._bot = bot_instance
 67
 68    async def __aenter__(self) -> "OnlineConfig":
 69        """Enter the 'async with' statement.
 70
 71        This method is called when entering the 'async with' statement.
 72
 73        Returns:
 74            OnlineConfig: The database session layer.
 75        """
 76        return self
 77
 78    async def __aexit__(self, exc_type: Type[BaseException] | None, exc_value: BaseException | None,
 79                        traceback: types.TracebackType | None) -> None:
 80        """Exit the 'async with' statement.
 81
 82        This method is called when exiting the 'async with' statement.
 83        Or when an exception is raised.
 84        We roll back the session if an exception is raised.
 85        We then close the session regardless.
 86
 87        Args:
 88            exc_type: The exception type.
 89            exc_value: The exception value.
 90            traceback: The traceback.
 91        """
 92        if exc_type:
 93            await self.rollback()
 94        await self.close()
 95
 96    async def close(self) -> None:
 97        """Close the database session.
 98
 99        This method closes the underlying SQLAlchemy session.
100        """
101        await self._session.close()
102
103    async def flush(self) -> None:
104        """Flush the database session.
105
106        Moves all the changes to the database transaction.
107        """
108        await self._session.flush()
109
110    async def commit(self) -> None:
111        """Commit the database session.
112
113        Commits the underlying SQLAlchemy session.
114        """
115        await self._session.commit()
116
117    async def rollback(self) -> None:
118        """Rollback the database session.
119
120        Cleans all the non-committed changes.
121        This includes the flushed changes.
122        """
123        await self._session.rollback()
124
125    async def delete(self, obj) -> None:
126        """Delete a row from the database.
127
128        Marks the object for deletion.
129        The deletion will be done on the next flush.
130        """
131        await self._session.delete(obj)
132
133    async def get_guild(self, guild_id: int, options: Sequence[base.ExecutableOption] | None = None) -> models.Guild:
134        """Get or create a guild from the database.
135
136        Fetches a guild from the database.
137        Due to the convenience of the database layer,
138        we guarantee a guild will always be returned.
139        It will be created if it does not exist.
140        However, we do not commit the changes.
141
142        Args:
143            guild_id: The guild ID.
144            options: The options to use when querying the database.
145                Those options are mostly used for relationship loading.
146                As in async, we can't use lazy loading.
147
148        Returns:
149            models.Guild: The guild.
150                With the relationships loaded if options are provided.
151                Otherwise, attempting to access the relationships will
152                result in an error.
153        """
154        if options:
155            guild_conf = await self._session.scalar(
156                sql.select(models.Guild).where(models.Guild.guild_id == guild_id).options(*options))
157        else:
158            guild_conf = await self._session.scalar(sql.select(models.Guild).where(models.Guild.guild_id == guild_id))
159        if guild_conf is None:
160            guild_conf = models.Guild(guild_id=guild_id)
161            self._session.add(guild_conf)
162        return guild_conf
163
164    async def get_user(self, user_id: int, options: Sequence[base.ExecutableOption] | None = None) -> models.User:
165        """Get or create a user from the database.
166
167        Fetches a user from the database.
168        Due to the convenience of the database layer,
169        we guarantee a user will always be returned.
170        It will be created if it does not exist.
171        However, we do not commit the changes.
172
173        Args:
174            user_id: The user ID.
175            options: The options to use when querying the database.
176                Those options are mostly used for relationship loading.
177                As in async, we can't use lazy loading.
178
179        Returns:
180            models.User: The user.
181                With the relationships loaded if options are provided.
182                Otherwise, attempting to access the relationships will
183                result in an error.
184        """
185        if options:
186            user = await self._session.scalar(
187                sql.select(models.User).where(models.User.user_id == user_id).options(*options))
188        else:
189            user = await self._session.scalar(sql.select(models.User).where(models.User.user_id == user_id))
190        if user is None:
191            user = models.User(user_id=user_id)
192            self._session.add(user)
193        return user
194
195    async def get_member(self, user_id: int, guild_id: int) -> models.Member:
196        """Get or create a member from the database.
197
198        Fetches a member from the database.
199        If the member does not exist, it will be created.
200        We also check if the guild exists and create it if it does not.
201        We also check if the user exists and create it if it does not.
202
203        Args:
204            user_id: The user ID.
205            guild_id: The guild ID.
206
207        Returns:
208            models.Member: The member.
209                As members have a one-to-one relationship with guilds,
210                and a one-to-one relationship with users, we automatically
211                load the guild and user relationships.
212        """
213        guild = await self.get_guild(guild_id)
214        user = await self.get_user(user_id)
215        member_conf = await self._session.scalar(
216            sql.select(models.Member).where(models.Member.user == user, models.Member.guild == guild))
217        if member_conf is None:
218            member_conf = models.Member(user=user, guild=guild)
219            self._session.add(member_conf)
220        return member_conf
221
222    async def get_expired_members(self) -> Sequence[models.Member]:
223        """Get expired members from the database.
224
225        Fetches all members with expired status from the database.
226        Used to clean up the roles.
227
228        Returns:
229            Sequence[models.Member]: The members with expired status.
230        """
231        time = datetime.datetime.utcnow()
232        expr_members = await self._session.scalars(sql.select(models.Member).where(models.Member.status_till <= time))
233        return expr_members.all()
234
235    async def get_ticket_bot(self, user_id: int, guild_id: int) -> Tuple[bool, models.TicketBot]:
236        """Get or create a ticket bot from the database.
237
238        Fetches a ticket bot from the database.
239        If the ticket bot does not exist, it will be created.
240        We also check if the guild exists and create it if it does not.
241
242        Args:
243            user_id: The user ID.
244            guild_id: The guild ID.
245
246        Returns:
247            Tuple[bool, models.TicketBot]: A tuple containing a boolean
248                indicating if the ticket bot was created, and the ticket bot.
249                Relationships are loaded.
250        """
251        guild = await self.get_guild(guild_id)
252        ticket_user = await self._session.scalar(
253            sql.select(models.TicketBot).where(models.TicketBot.user_id == user_id, models.TicketBot.guild == guild))
254        new = False
255        if ticket_user is None:
256            new = True
257            ticket_user = models.TicketBot(user_id=user_id, guild=guild)
258            self._session.add(ticket_user)
259        return new, ticket_user
260
261    async def check_ticket_bot(self, user_id: int, guild_id: int) -> bool:
262        """Check if the ticket user exists.
263
264        A more efficient way to check if a ticket user exists.
265        Instead of attempting to create the ticket user,
266        we just check if it exists.
267        This is the only check that uses a guild ID.
268        This is because the user ID is shared across all guilds.
269
270        Args:
271            user_id: The user ID.
272            guild_id: The guild ID.
273
274        Returns:
275            bool: A boolean indicating if the ticket user exists.
276        """
277        ticket_user = await self._session.scalar(
278            sql.select(models.TicketBot).where(models.TicketBot.user_id == user_id,
279                                               models.TicketBot.guild_id == guild_id))
280        return ticket_user is not None
281
282    async def get_ticket_type(self,
283                              guild_id: int,
284                              name: str,
285                              comping: bool = False,
286                              comaccs: bool = False,
287                              strpbuttns: bool = False,
288                              ignore: bool = False) -> Tuple[bool, models.TicketType]:
289        """Get or create a ticket type from the database.
290
291        Fetches a ticket type from the database.
292        If the ticket type does not exist, it will be created.
293        We also check if the guild exists and create it if it does not.
294
295        Args:
296            guild_id: The guild ID.
297            name: The ticket type name.
298            comping: The comping flag.
299            comaccs: The comaccs flag.
300            strpbuttns: The strpbuttns flag.
301            ignore: The ignore flag.
302
303        Returns:
304            Tuple[bool, models.TicketType]: A tuple containing a boolean
305                indicating if the ticket type was created, and the ticket type.
306                Relationships are loaded.
307        """
308        guild = await self.get_guild(guild_id)
309        ticket_type = await self._session.scalar(
310            sql.select(models.TicketType).where(models.TicketType.guild == guild, models.TicketType.prefix == name))
311        new = False
312        if ticket_type is None:
313            new = True
314            ticket_type = models.TicketType(guild=guild,
315                                            prefix=name,
316                                            comping=comping,
317                                            comaccs=comaccs,
318                                            strpbuttns=strpbuttns,
319                                            ignore=ignore)
320            self._session.add(ticket_type)
321        return new, ticket_type
322
323    async def get_ticket_types(self, guild_id: int) -> Sequence[models.TicketType]:
324        """Get ticket types from the database.
325
326        Fetches all ticket types from the database.
327        We also check if the guild exists and create it if it does not.
328
329        Args:
330            guild_id: The guild ID.
331
332        Returns:
333            Sequence[models.TicketType]: The ticket types.
334        """
335        guild = await self.get_guild(guild_id)
336        ticket_types = await self._session.scalars(
337            sql.select(models.TicketType).where(models.TicketType.guild == guild))
338        return ticket_types.all()
339
340    async def fetch_ticket(self, channel_id: int) -> models.Ticket | None:
341        """Fetch a ticket from the database.
342
343        Attempts to fetch a ticket from the database.
344        If the ticket does not exist, None is returned.
345        If you want to create a ticket if it does not exist,
346        use get_ticket instead.
347
348        Args:
349            channel_id: The channel ID.
350
351        Returns:
352            models.Ticket | None: The ticket.
353        """
354        ticket = await self._session.get(models.Ticket, channel_id)
355        return ticket
356
357    async def get_ticket(self,
358                         channel_id: int,
359                         guild_id: int,
360                         user_id: int | None = None,
361                         staff_note: int | None = None) -> Tuple[bool, models.Ticket]:
362        """Get or create a ticket from the database.
363
364        Fetches a ticket from the database.
365        If the ticket does not exist, it will be created.
366        We also check if the guild exists and create it if it does not.
367        If you want to check if a ticket exists, use fetch_ticket instead.
368
369        Args:
370            channel_id: The channel ID.
371            guild_id: The guild ID. Used to create the guild if missing.
372            user_id: The user ID. Only filed if integration is enabled.
373            staff_note: The staff note thread ID. Used to annotate the thread,
374                if the ticket is created.
375
376        Returns:
377            Tuple[bool, models.Ticket]: A tuple containing a boolean
378                indicating if the ticket was created, and the ticket.
379                Relationships are loaded.
380        """
381        guild = await self.get_guild(guild_id)
382        ticket = await self._session.get(models.Ticket, channel_id)
383        new = False
384        if ticket is None:
385            new = True
386            ticket = models.Ticket(channel_id=channel_id, guild=guild, user_id=user_id, staff_note_thread=staff_note)
387            self._session.add(ticket)
388        return new, ticket
389
390    async def get_pending_tickets(self) -> Sequence[models.Ticket]:
391        """Get pending tickets from the database.
392
393        Fetches all pending tickets from the database.
394
395        Returns:
396            Sequence[models.Ticket]: The pending tickets.
397        """
398        tickets = await self._session.scalars(
399            sql.select(models.Ticket).join(models.Guild).filter(
400                models.Guild.warn_autoclose.isnot(None), models.Ticket.notified.isnot(True), models.Ticket.last_response
401                <= models.UTCnow() - models.Guild.warn_autoclose))
402        return tickets.all()
403
404    async def fetch_tag(self, guild_id: int, tag: str) -> discord.Embed | str | None:
405        """Fetch a tag from the database.
406
407        Attempts to fetch a tag from the database.
408        If the tag does not exist, None is returned.
409        If you want to create a tag if it does not exist,
410        use get_tag instead.
411
412        Args:
413            guild_id: The guild ID.
414            tag: The tag.
415
416        Returns:
417            discord.Embed | str | None: The tag.
418        """
419        guild = await self.get_guild(guild_id)
420        embed = await self._session.scalar(
421            sql.select(models.Tag).where(models.Tag.guild == guild, models.Tag.tag_name == tag))
422        if embed is None:
423            return None
424        if embed.title:
425            emdd = vars(embed)
426            emd2 = {}
427            for key, data in emdd.items():
428                if data is not None:
429                    emd2[key] = data
430            if embed.author:
431                emd2["author"] = {"name": embed.author}
432            if embed.footer:
433                emd2["footer"] = {"text": embed.footer}
434            result = discord.Embed.from_dict(emd2)
435            result.timestamp = utils.utcnow()
436            return result
437        return embed.description
438
439    async def get_tag(
440        self,
441        guild_id: int,
442        tag_name: str,
443        description: str,
444        embed_args: dict[str, Any] | None = None,
445    ) -> Tuple[bool, models.Tag]:
446        """Get or create a tag from the database.
447
448        Fetches a tag from the database.
449        If the tag does not exist, it will be created.
450        We also check if the guild exists and create it if it does not.
451        If you want to check if a tag exists, use fetch_tag instead.
452
453        Args:
454            guild_id: The guild ID.
455            tag_name: The tag.
456            description: The description.
457            embed_args: The embed arguments.
458                Basically, the arguments to pass to discord.Embed,
459                when using discord.Embed.from_dict.
460        """
461        guild = await self.get_guild(guild_id)
462        tag = await self._session.scalar(
463            sql.select(models.Tag).where(models.Tag.guild == guild, models.Tag.tag_name == tag_name))
464        new = False
465        if tag is None:
466            new = True
467            if embed_args is None:
468                embed_args = {}
469            tag = models.Tag(guild=guild, tag_name=tag_name, description=description, **embed_args)
470            self._session.add(tag)
471        return new, tag
472
473    async def get_tags(self, guild_id: int) -> Sequence[models.Tag]:
474        """Get tags from the database.
475
476        Fetches all tags from the database.
477        We also check if the guild exists and create it if it does not.
478
479        Args:
480            guild_id: The guild ID.
481
482        Returns:
483            Sequence[models.Tag]: The tags.
484        """
485        guild = await self.get_guild(guild_id)
486        tags = await self._session.scalars(sql.select(models.Tag).where(models.Tag.guild == guild))
487        return tags.all()
488
489    async def get_staff_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.StaffRole]:
490        """Get or create the staff role from the database.
491
492        Fetches a staff role from the database.
493        If the staff role does not exist, it will be created.
494        We also check if the guild exists and create it if it does not.
495
496        Args:
497            role_id: The role ID.
498            guild_id: The guild ID.
499
500        Returns:
501            Tuple[bool, models.StaffRole]: A tuple containing a boolean
502                indicating if the staff role was created, and the staff role.
503                Relationships are loaded.
504        """
505        guild = await self.get_guild(guild_id)
506        staff_role = await self._session.get(models.StaffRole, role_id)
507        new = False
508        if staff_role is None:
509            new = True
510            staff_role = models.StaffRole(role_id=role_id, guild=guild)
511            self._session.add(staff_role)
512        return new, staff_role
513
514    async def get_all_staff_roles(self, guild_id: int) -> Sequence[models.StaffRole]:
515        """Get all staff roles from the database.
516
517        Fetches all staff roles from the database.
518        If the staff roles do not exist, an empty list is returned.
519
520        Args:
521            guild_id: The guild ID.
522
523        Returns:
524            Sequence[models.StaffRole]: A list of staff roles.
525                Relationships are loaded.
526        """
527        guild = await self.get_guild(guild_id)
528        staff_roles = await self._session.scalars(sql.select(models.StaffRole).where(models.StaffRole.guild == guild))
529        return staff_roles.all()
530
531    async def check_staff_role(self, role_id: int) -> bool:
532        """Check if the staff role exists.
533
534        A more efficient way to check if a staff role exists.
535        Instead of attempting to create the staff role,
536        we just check if it exists.
537
538        Args:
539            role_id: The role ID.
540
541        Returns:
542            bool: A boolean indicating if the staff role exists.
543        """
544        staff_role = await self._session.get(models.StaffRole, role_id)
545        return staff_role is not None
546
547    async def get_observers_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.ObserversRole]:
548        """Get or create the observer role from the database.
549
550        Fetches an observer role from the database.
551        If the observer role does not exist, it will be created.
552        We also check if the guild exists and create it if it does not.
553
554        Args:
555            role_id: The role ID.
556            guild_id: The guild ID.
557
558        Returns:
559            Tuple[bool, models.ObserversRole]: A tuple containing a boolean
560                indicating if the observer role was created, and the observers'
561                role. Relationships are loaded.
562        """
563        guild = await self.get_guild(guild_id)
564        observers_role = await self._session.get(models.ObserversRole, role_id)
565        new = False
566        if observers_role is None:
567            new = True
568            observers_role = models.ObserversRole(role_id=role_id, guild=guild)
569            self._session.add(observers_role)
570        return new, observers_role
571
572    async def get_all_observers_roles(self, guild_id: int) -> Sequence[models.ObserversRole]:
573        """Get all observer roles from the database.
574
575        Fetches all observer roles from the database.
576        If the observer roles do not exist, an empty list is returned.
577
578        Args:
579            guild_id: The guild ID.
580
581        Returns:
582            Sequence[models.ObserversRole]: A list of observer roles.
583                Relationships are loaded.
584        """
585        guild = await self.get_guild(guild_id)
586        observers_roles = await self._session.scalars(
587            sql.select(models.ObserversRole).where(models.ObserversRole.guild == guild))
588        return observers_roles.all()
589
590    async def check_observers_role(self, role_id: int) -> bool:
591        """Check if the observer role exists.
592
593        A more efficient way to check if an observer role exists.
594        Instead of attempting to create the observer role,
595        we just check if it exists.
596
597        Args:
598            role_id: The role ID.
599
600        Returns:
601            bool: A boolean indicating if the observer role exists.
602        """
603        observers_role = await self._session.get(models.ObserversRole, role_id)
604        return observers_role is not None
605
606    async def get_community_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.CommunityRole]:
607        """Get or create the community role from the database.
608
609        Fetches a community role from the database.
610        If the community role does not exist, it will be created.
611        We also check if the guild exists and create it if it does not.
612
613        Args:
614            role_id: The role ID.
615            guild_id: The guild ID.
616
617        Returns:
618            Tuple[bool, models.CommunityRole]: A tuple containing a boolean
619                indicating if the community role was created, and the community
620                role. Relationships are loaded.
621        """
622        guild = await self.get_guild(guild_id)
623        community_role = await self._session.get(models.CommunityRole, role_id)
624        new = False
625        if community_role is None:
626            new = True
627            community_role = models.CommunityRole(role_id=role_id, guild=guild)
628            self._session.add(community_role)
629        return new, community_role
630
631    async def get_all_community_roles(self, guild_id: int) -> Sequence[models.CommunityRole]:
632        """Get all community roles from the database.
633
634        Fetches all community roles from the database.
635        If the community roles do not exist, an empty list is returned.
636
637        Args:
638            guild_id: The guild ID.
639
640        Returns:
641            Sequence[models.CommunityRole]: A list of community roles.
642                Relationships are loaded.
643        """
644        guild = await self.get_guild(guild_id)
645        community_roles = await self._session.scalars(
646            sql.select(models.CommunityRole).where(models.CommunityRole.guild == guild))
647        return community_roles.all()
648
649    async def check_community_role(self, role_id: int) -> bool:
650        """Check if the community role exists.
651
652        A more efficient way to check if a community role exists.
653        Instead of attempting to create the community role,
654        we just check if it exists.
655
656        Args:
657            role_id: The role ID.
658
659        Returns:
660            bool: A boolean indicating if the community role exists.
661        """
662        community_role = await self._session.get(models.CommunityRole, role_id)
663        return community_role is not None
664
665    async def get_community_ping(self, role_id: int, guild_id: int) -> Tuple[bool, models.CommunityPing]:
666        """Get or create the community ping from the database.
667
668        Fetches a community ping from the database.
669        If the community ping does not exist, it will be created.
670        We also check if the guild exists and create it if it does not.
671
672        Args:
673            role_id: The role ID.
674            guild_id: The guild ID.
675
676        Returns:
677            Tuple[bool, models.CommunityPing]: A tuple containing a boolean
678                indicating if the community ping was created, and the community
679                pings. Relationships are loaded.
680        """
681        guild = await self.get_guild(guild_id)
682        community_ping = await self._session.get(models.CommunityPing, role_id)
683        new = False
684        if community_ping is None:
685            new = True
686            community_ping = models.CommunityPing(role_id=role_id, guild=guild)
687            self._session.add(community_ping)
688        return new, community_ping
689
690    async def get_all_community_pings(self, guild_id: int) -> Sequence[models.CommunityPing]:
691        """Get all community pings from the database.
692
693        Fetches all community pings from the database.
694        If the community pings do not exist, an empty list is returned.
695
696        Args:
697            guild_id: The guild ID.
698
699        Returns:
700            Sequence[models.CommunityPing]: A list of community pings.
701                Relationships are loaded.
702        """
703        guild = await self.get_guild(guild_id)
704        community_pings = await self._session.scalars(
705            sql.select(models.CommunityPing).where(models.CommunityPing.guild == guild))
706        return community_pings.all()
707
708    async def check_community_ping(self, role_id: int) -> bool:
709        """Check if the community ping exists.
710
711        A more efficient way to check if a community ping exists.
712        Instead of attempting to create the community ping,
713        we just check if it exists.
714
715        Args:
716            role_id: The role ID.
717
718        Returns:
719            bool: A boolean indicating if the community ping exists.
720        """
721        community_ping = await self._session.get(models.CommunityPing, role_id)
722        return community_ping is not None

A convenience layer for the database session.

This class is used to make the database session easier to use. It also handles the 'async with' statement. Any and all commits have to be done manually.

OnlineConfig( bot_instance: discord.ext.commands.bot.AutoShardedBot, session: sqlalchemy.ext.asyncio.session.AsyncSession)
55    def __init__(self, bot_instance: commands.AutoShardedBot, session: sa_asyncio.AsyncSession) -> None:
56        """Initialises the database session layer.
57
58        Wraps the provided session in an async context manager.
59        And set the bot instance as a private attribute.
60
61        Args:
62            bot_instance: The bot instance.
63            session: The database session.
64        """
65        self._session = session
66        self._bot = bot_instance

Initialises the database session layer.

Wraps the provided session in an async context manager. And set the bot instance as a private attribute.

Arguments:
  • bot_instance: The bot instance.
  • session: The database session.
async def close(self) -> None:
 96    async def close(self) -> None:
 97        """Close the database session.
 98
 99        This method closes the underlying SQLAlchemy session.
100        """
101        await self._session.close()

Close the database session.

This method closes the underlying SQLAlchemy session.

async def flush(self) -> None:
103    async def flush(self) -> None:
104        """Flush the database session.
105
106        Moves all the changes to the database transaction.
107        """
108        await self._session.flush()

Flush the database session.

Moves all the changes to the database transaction.

async def commit(self) -> None:
110    async def commit(self) -> None:
111        """Commit the database session.
112
113        Commits the underlying SQLAlchemy session.
114        """
115        await self._session.commit()

Commit the database session.

Commits the underlying SQLAlchemy session.

async def rollback(self) -> None:
117    async def rollback(self) -> None:
118        """Rollback the database session.
119
120        Cleans all the non-committed changes.
121        This includes the flushed changes.
122        """
123        await self._session.rollback()

Rollback the database session.

Cleans all the non-committed changes. This includes the flushed changes.

async def delete(self, obj) -> None:
125    async def delete(self, obj) -> None:
126        """Delete a row from the database.
127
128        Marks the object for deletion.
129        The deletion will be done on the next flush.
130        """
131        await self._session.delete(obj)

Delete a row from the database.

Marks the object for deletion. The deletion will be done on the next flush.

async def get_guild( self, guild_id: int, options: Optional[Sequence[sqlalchemy.sql.base.ExecutableOption]] = None) -> tickets_plus.database.models.Guild:
133    async def get_guild(self, guild_id: int, options: Sequence[base.ExecutableOption] | None = None) -> models.Guild:
134        """Get or create a guild from the database.
135
136        Fetches a guild from the database.
137        Due to the convenience of the database layer,
138        we guarantee a guild will always be returned.
139        It will be created if it does not exist.
140        However, we do not commit the changes.
141
142        Args:
143            guild_id: The guild ID.
144            options: The options to use when querying the database.
145                Those options are mostly used for relationship loading.
146                As in async, we can't use lazy loading.
147
148        Returns:
149            models.Guild: The guild.
150                With the relationships loaded if options are provided.
151                Otherwise, attempting to access the relationships will
152                result in an error.
153        """
154        if options:
155            guild_conf = await self._session.scalar(
156                sql.select(models.Guild).where(models.Guild.guild_id == guild_id).options(*options))
157        else:
158            guild_conf = await self._session.scalar(sql.select(models.Guild).where(models.Guild.guild_id == guild_id))
159        if guild_conf is None:
160            guild_conf = models.Guild(guild_id=guild_id)
161            self._session.add(guild_conf)
162        return guild_conf

Get or create a guild from the database.

Fetches a guild from the database. Due to the convenience of the database layer, we guarantee a guild will always be returned. It will be created if it does not exist. However, we do not commit the changes.

Arguments:
  • guild_id: The guild ID.
  • options: The options to use when querying the database. Those options are mostly used for relationship loading. As in async, we can't use lazy loading.
Returns:

models.Guild: The guild. With the relationships loaded if options are provided. Otherwise, attempting to access the relationships will result in an error.

async def get_user( self, user_id: int, options: Optional[Sequence[sqlalchemy.sql.base.ExecutableOption]] = None) -> tickets_plus.database.models.User:
164    async def get_user(self, user_id: int, options: Sequence[base.ExecutableOption] | None = None) -> models.User:
165        """Get or create a user from the database.
166
167        Fetches a user from the database.
168        Due to the convenience of the database layer,
169        we guarantee a user will always be returned.
170        It will be created if it does not exist.
171        However, we do not commit the changes.
172
173        Args:
174            user_id: The user ID.
175            options: The options to use when querying the database.
176                Those options are mostly used for relationship loading.
177                As in async, we can't use lazy loading.
178
179        Returns:
180            models.User: The user.
181                With the relationships loaded if options are provided.
182                Otherwise, attempting to access the relationships will
183                result in an error.
184        """
185        if options:
186            user = await self._session.scalar(
187                sql.select(models.User).where(models.User.user_id == user_id).options(*options))
188        else:
189            user = await self._session.scalar(sql.select(models.User).where(models.User.user_id == user_id))
190        if user is None:
191            user = models.User(user_id=user_id)
192            self._session.add(user)
193        return user

Get or create a user from the database.

Fetches a user from the database. Due to the convenience of the database layer, we guarantee a user will always be returned. It will be created if it does not exist. However, we do not commit the changes.

Arguments:
  • user_id: The user ID.
  • options: The options to use when querying the database. Those options are mostly used for relationship loading. As in async, we can't use lazy loading.
Returns:

models.User: The user. With the relationships loaded if options are provided. Otherwise, attempting to access the relationships will result in an error.

async def get_member(self, user_id: int, guild_id: int) -> tickets_plus.database.models.Member:
195    async def get_member(self, user_id: int, guild_id: int) -> models.Member:
196        """Get or create a member from the database.
197
198        Fetches a member from the database.
199        If the member does not exist, it will be created.
200        We also check if the guild exists and create it if it does not.
201        We also check if the user exists and create it if it does not.
202
203        Args:
204            user_id: The user ID.
205            guild_id: The guild ID.
206
207        Returns:
208            models.Member: The member.
209                As members have a one-to-one relationship with guilds,
210                and a one-to-one relationship with users, we automatically
211                load the guild and user relationships.
212        """
213        guild = await self.get_guild(guild_id)
214        user = await self.get_user(user_id)
215        member_conf = await self._session.scalar(
216            sql.select(models.Member).where(models.Member.user == user, models.Member.guild == guild))
217        if member_conf is None:
218            member_conf = models.Member(user=user, guild=guild)
219            self._session.add(member_conf)
220        return member_conf

Get or create a member from the database.

Fetches a member from the database. If the member does not exist, it will be created. We also check if the guild exists and create it if it does not. We also check if the user exists and create it if it does not.

Arguments:
  • user_id: The user ID.
  • guild_id: The guild ID.
Returns:

models.Member: The member. As members have a one-to-one relationship with guilds, and a one-to-one relationship with users, we automatically load the guild and user relationships.

async def get_expired_members(self) -> Sequence[tickets_plus.database.models.Member]:
222    async def get_expired_members(self) -> Sequence[models.Member]:
223        """Get expired members from the database.
224
225        Fetches all members with expired status from the database.
226        Used to clean up the roles.
227
228        Returns:
229            Sequence[models.Member]: The members with expired status.
230        """
231        time = datetime.datetime.utcnow()
232        expr_members = await self._session.scalars(sql.select(models.Member).where(models.Member.status_till <= time))
233        return expr_members.all()

Get expired members from the database.

Fetches all members with expired status from the database. Used to clean up the roles.

Returns:

Sequence[models.Member]: The members with expired status.

async def get_ticket_bot( self, user_id: int, guild_id: int) -> Tuple[bool, tickets_plus.database.models.TicketBot]:
235    async def get_ticket_bot(self, user_id: int, guild_id: int) -> Tuple[bool, models.TicketBot]:
236        """Get or create a ticket bot from the database.
237
238        Fetches a ticket bot from the database.
239        If the ticket bot does not exist, it will be created.
240        We also check if the guild exists and create it if it does not.
241
242        Args:
243            user_id: The user ID.
244            guild_id: The guild ID.
245
246        Returns:
247            Tuple[bool, models.TicketBot]: A tuple containing a boolean
248                indicating if the ticket bot was created, and the ticket bot.
249                Relationships are loaded.
250        """
251        guild = await self.get_guild(guild_id)
252        ticket_user = await self._session.scalar(
253            sql.select(models.TicketBot).where(models.TicketBot.user_id == user_id, models.TicketBot.guild == guild))
254        new = False
255        if ticket_user is None:
256            new = True
257            ticket_user = models.TicketBot(user_id=user_id, guild=guild)
258            self._session.add(ticket_user)
259        return new, ticket_user

Get or create a ticket bot from the database.

Fetches a ticket bot from the database. If the ticket bot does not exist, it will be created. We also check if the guild exists and create it if it does not.

Arguments:
  • user_id: The user ID.
  • guild_id: The guild ID.
Returns:

Tuple[bool, models.TicketBot]: A tuple containing a boolean indicating if the ticket bot was created, and the ticket bot. Relationships are loaded.

async def check_ticket_bot(self, user_id: int, guild_id: int) -> bool:
261    async def check_ticket_bot(self, user_id: int, guild_id: int) -> bool:
262        """Check if the ticket user exists.
263
264        A more efficient way to check if a ticket user exists.
265        Instead of attempting to create the ticket user,
266        we just check if it exists.
267        This is the only check that uses a guild ID.
268        This is because the user ID is shared across all guilds.
269
270        Args:
271            user_id: The user ID.
272            guild_id: The guild ID.
273
274        Returns:
275            bool: A boolean indicating if the ticket user exists.
276        """
277        ticket_user = await self._session.scalar(
278            sql.select(models.TicketBot).where(models.TicketBot.user_id == user_id,
279                                               models.TicketBot.guild_id == guild_id))
280        return ticket_user is not None

Check if the ticket user exists.

A more efficient way to check if a ticket user exists. Instead of attempting to create the ticket user, we just check if it exists. This is the only check that uses a guild ID. This is because the user ID is shared across all guilds.

Arguments:
  • user_id: The user ID.
  • guild_id: The guild ID.
Returns:

bool: A boolean indicating if the ticket user exists.

async def get_ticket_type( self, guild_id: int, name: str, comping: bool = False, comaccs: bool = False, strpbuttns: bool = False, ignore: bool = False) -> Tuple[bool, tickets_plus.database.models.TicketType]:
282    async def get_ticket_type(self,
283                              guild_id: int,
284                              name: str,
285                              comping: bool = False,
286                              comaccs: bool = False,
287                              strpbuttns: bool = False,
288                              ignore: bool = False) -> Tuple[bool, models.TicketType]:
289        """Get or create a ticket type from the database.
290
291        Fetches a ticket type from the database.
292        If the ticket type does not exist, it will be created.
293        We also check if the guild exists and create it if it does not.
294
295        Args:
296            guild_id: The guild ID.
297            name: The ticket type name.
298            comping: The comping flag.
299            comaccs: The comaccs flag.
300            strpbuttns: The strpbuttns flag.
301            ignore: The ignore flag.
302
303        Returns:
304            Tuple[bool, models.TicketType]: A tuple containing a boolean
305                indicating if the ticket type was created, and the ticket type.
306                Relationships are loaded.
307        """
308        guild = await self.get_guild(guild_id)
309        ticket_type = await self._session.scalar(
310            sql.select(models.TicketType).where(models.TicketType.guild == guild, models.TicketType.prefix == name))
311        new = False
312        if ticket_type is None:
313            new = True
314            ticket_type = models.TicketType(guild=guild,
315                                            prefix=name,
316                                            comping=comping,
317                                            comaccs=comaccs,
318                                            strpbuttns=strpbuttns,
319                                            ignore=ignore)
320            self._session.add(ticket_type)
321        return new, ticket_type

Get or create a ticket type from the database.

Fetches a ticket type from the database. If the ticket type does not exist, it will be created. We also check if the guild exists and create it if it does not.

Arguments:
  • guild_id: The guild ID.
  • name: The ticket type name.
  • comping: The comping flag.
  • comaccs: The comaccs flag.
  • strpbuttns: The strpbuttns flag.
  • ignore: The ignore flag.
Returns:

Tuple[bool, models.TicketType]: A tuple containing a boolean indicating if the ticket type was created, and the ticket type. Relationships are loaded.

async def get_ticket_types(self, guild_id: int) -> Sequence[tickets_plus.database.models.TicketType]:
323    async def get_ticket_types(self, guild_id: int) -> Sequence[models.TicketType]:
324        """Get ticket types from the database.
325
326        Fetches all ticket types from the database.
327        We also check if the guild exists and create it if it does not.
328
329        Args:
330            guild_id: The guild ID.
331
332        Returns:
333            Sequence[models.TicketType]: The ticket types.
334        """
335        guild = await self.get_guild(guild_id)
336        ticket_types = await self._session.scalars(
337            sql.select(models.TicketType).where(models.TicketType.guild == guild))
338        return ticket_types.all()

Get ticket types from the database.

Fetches all ticket types from the database. We also check if the guild exists and create it if it does not.

Arguments:
  • guild_id: The guild ID.
Returns:

Sequence[models.TicketType]: The ticket types.

async def fetch_ticket(self, channel_id: int) -> tickets_plus.database.models.Ticket | None:
340    async def fetch_ticket(self, channel_id: int) -> models.Ticket | None:
341        """Fetch a ticket from the database.
342
343        Attempts to fetch a ticket from the database.
344        If the ticket does not exist, None is returned.
345        If you want to create a ticket if it does not exist,
346        use get_ticket instead.
347
348        Args:
349            channel_id: The channel ID.
350
351        Returns:
352            models.Ticket | None: The ticket.
353        """
354        ticket = await self._session.get(models.Ticket, channel_id)
355        return ticket

Fetch a ticket from the database.

Attempts to fetch a ticket from the database. If the ticket does not exist, None is returned. If you want to create a ticket if it does not exist, use get_ticket instead.

Arguments:
  • channel_id: The channel ID.
Returns:

models.Ticket | None: The ticket.

async def get_ticket( self, channel_id: int, guild_id: int, user_id: int | None = None, staff_note: int | None = None) -> Tuple[bool, tickets_plus.database.models.Ticket]:
357    async def get_ticket(self,
358                         channel_id: int,
359                         guild_id: int,
360                         user_id: int | None = None,
361                         staff_note: int | None = None) -> Tuple[bool, models.Ticket]:
362        """Get or create a ticket from the database.
363
364        Fetches a ticket from the database.
365        If the ticket does not exist, it will be created.
366        We also check if the guild exists and create it if it does not.
367        If you want to check if a ticket exists, use fetch_ticket instead.
368
369        Args:
370            channel_id: The channel ID.
371            guild_id: The guild ID. Used to create the guild if missing.
372            user_id: The user ID. Only filed if integration is enabled.
373            staff_note: The staff note thread ID. Used to annotate the thread,
374                if the ticket is created.
375
376        Returns:
377            Tuple[bool, models.Ticket]: A tuple containing a boolean
378                indicating if the ticket was created, and the ticket.
379                Relationships are loaded.
380        """
381        guild = await self.get_guild(guild_id)
382        ticket = await self._session.get(models.Ticket, channel_id)
383        new = False
384        if ticket is None:
385            new = True
386            ticket = models.Ticket(channel_id=channel_id, guild=guild, user_id=user_id, staff_note_thread=staff_note)
387            self._session.add(ticket)
388        return new, ticket

Get or create a ticket from the database.

Fetches a ticket from the database. If the ticket does not exist, it will be created. We also check if the guild exists and create it if it does not. If you want to check if a ticket exists, use fetch_ticket instead.

Arguments:
  • channel_id: The channel ID.
  • guild_id: The guild ID. Used to create the guild if missing.
  • user_id: The user ID. Only filed if integration is enabled.
  • staff_note: The staff note thread ID. Used to annotate the thread, if the ticket is created.
Returns:

Tuple[bool, models.Ticket]: A tuple containing a boolean indicating if the ticket was created, and the ticket. Relationships are loaded.

async def get_pending_tickets(self) -> Sequence[tickets_plus.database.models.Ticket]:
390    async def get_pending_tickets(self) -> Sequence[models.Ticket]:
391        """Get pending tickets from the database.
392
393        Fetches all pending tickets from the database.
394
395        Returns:
396            Sequence[models.Ticket]: The pending tickets.
397        """
398        tickets = await self._session.scalars(
399            sql.select(models.Ticket).join(models.Guild).filter(
400                models.Guild.warn_autoclose.isnot(None), models.Ticket.notified.isnot(True), models.Ticket.last_response
401                <= models.UTCnow() - models.Guild.warn_autoclose))
402        return tickets.all()

Get pending tickets from the database.

Fetches all pending tickets from the database.

Returns:

Sequence[models.Ticket]: The pending tickets.

async def fetch_tag(self, guild_id: int, tag: str) -> discord.embeds.Embed | str | None:
404    async def fetch_tag(self, guild_id: int, tag: str) -> discord.Embed | str | None:
405        """Fetch a tag from the database.
406
407        Attempts to fetch a tag from the database.
408        If the tag does not exist, None is returned.
409        If you want to create a tag if it does not exist,
410        use get_tag instead.
411
412        Args:
413            guild_id: The guild ID.
414            tag: The tag.
415
416        Returns:
417            discord.Embed | str | None: The tag.
418        """
419        guild = await self.get_guild(guild_id)
420        embed = await self._session.scalar(
421            sql.select(models.Tag).where(models.Tag.guild == guild, models.Tag.tag_name == tag))
422        if embed is None:
423            return None
424        if embed.title:
425            emdd = vars(embed)
426            emd2 = {}
427            for key, data in emdd.items():
428                if data is not None:
429                    emd2[key] = data
430            if embed.author:
431                emd2["author"] = {"name": embed.author}
432            if embed.footer:
433                emd2["footer"] = {"text": embed.footer}
434            result = discord.Embed.from_dict(emd2)
435            result.timestamp = utils.utcnow()
436            return result
437        return embed.description

Fetch a tag from the database.

Attempts to fetch a tag from the database. If the tag does not exist, None is returned. If you want to create a tag if it does not exist, use get_tag instead.

Arguments:
  • guild_id: The guild ID.
  • tag: The tag.
Returns:

discord.Embed | str | None: The tag.

async def get_tag( self, guild_id: int, tag_name: str, description: str, embed_args: dict[str, typing.Any] | None = None) -> Tuple[bool, tickets_plus.database.models.Tag]:
439    async def get_tag(
440        self,
441        guild_id: int,
442        tag_name: str,
443        description: str,
444        embed_args: dict[str, Any] | None = None,
445    ) -> Tuple[bool, models.Tag]:
446        """Get or create a tag from the database.
447
448        Fetches a tag from the database.
449        If the tag does not exist, it will be created.
450        We also check if the guild exists and create it if it does not.
451        If you want to check if a tag exists, use fetch_tag instead.
452
453        Args:
454            guild_id: The guild ID.
455            tag_name: The tag.
456            description: The description.
457            embed_args: The embed arguments.
458                Basically, the arguments to pass to discord.Embed,
459                when using discord.Embed.from_dict.
460        """
461        guild = await self.get_guild(guild_id)
462        tag = await self._session.scalar(
463            sql.select(models.Tag).where(models.Tag.guild == guild, models.Tag.tag_name == tag_name))
464        new = False
465        if tag is None:
466            new = True
467            if embed_args is None:
468                embed_args = {}
469            tag = models.Tag(guild=guild, tag_name=tag_name, description=description, **embed_args)
470            self._session.add(tag)
471        return new, tag

Get or create a tag from the database.

Fetches a tag from the database. If the tag does not exist, it will be created. We also check if the guild exists and create it if it does not. If you want to check if a tag exists, use fetch_tag instead.

Arguments:
  • guild_id: The guild ID.
  • tag_name: The tag.
  • description: The description.
  • embed_args: The embed arguments. Basically, the arguments to pass to discord.Embed, when using discord.Embed.from_dict.
async def get_tags(self, guild_id: int) -> Sequence[tickets_plus.database.models.Tag]:
473    async def get_tags(self, guild_id: int) -> Sequence[models.Tag]:
474        """Get tags from the database.
475
476        Fetches all tags from the database.
477        We also check if the guild exists and create it if it does not.
478
479        Args:
480            guild_id: The guild ID.
481
482        Returns:
483            Sequence[models.Tag]: The tags.
484        """
485        guild = await self.get_guild(guild_id)
486        tags = await self._session.scalars(sql.select(models.Tag).where(models.Tag.guild == guild))
487        return tags.all()

Get tags from the database.

Fetches all tags from the database. We also check if the guild exists and create it if it does not.

Arguments:
  • guild_id: The guild ID.
Returns:

Sequence[models.Tag]: The tags.

async def get_staff_role( self, role_id: int, guild_id: int) -> Tuple[bool, tickets_plus.database.models.StaffRole]:
489    async def get_staff_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.StaffRole]:
490        """Get or create the staff role from the database.
491
492        Fetches a staff role from the database.
493        If the staff role does not exist, it will be created.
494        We also check if the guild exists and create it if it does not.
495
496        Args:
497            role_id: The role ID.
498            guild_id: The guild ID.
499
500        Returns:
501            Tuple[bool, models.StaffRole]: A tuple containing a boolean
502                indicating if the staff role was created, and the staff role.
503                Relationships are loaded.
504        """
505        guild = await self.get_guild(guild_id)
506        staff_role = await self._session.get(models.StaffRole, role_id)
507        new = False
508        if staff_role is None:
509            new = True
510            staff_role = models.StaffRole(role_id=role_id, guild=guild)
511            self._session.add(staff_role)
512        return new, staff_role

Get or create the staff role from the database.

Fetches a staff role from the database. If the staff role does not exist, it will be created. We also check if the guild exists and create it if it does not.

Arguments:
  • role_id: The role ID.
  • guild_id: The guild ID.
Returns:

Tuple[bool, models.StaffRole]: A tuple containing a boolean indicating if the staff role was created, and the staff role. Relationships are loaded.

async def get_all_staff_roles(self, guild_id: int) -> Sequence[tickets_plus.database.models.StaffRole]:
514    async def get_all_staff_roles(self, guild_id: int) -> Sequence[models.StaffRole]:
515        """Get all staff roles from the database.
516
517        Fetches all staff roles from the database.
518        If the staff roles do not exist, an empty list is returned.
519
520        Args:
521            guild_id: The guild ID.
522
523        Returns:
524            Sequence[models.StaffRole]: A list of staff roles.
525                Relationships are loaded.
526        """
527        guild = await self.get_guild(guild_id)
528        staff_roles = await self._session.scalars(sql.select(models.StaffRole).where(models.StaffRole.guild == guild))
529        return staff_roles.all()

Get all staff roles from the database.

Fetches all staff roles from the database. If the staff roles do not exist, an empty list is returned.

Arguments:
  • guild_id: The guild ID.
Returns:

Sequence[models.StaffRole]: A list of staff roles. Relationships are loaded.

async def check_staff_role(self, role_id: int) -> bool:
531    async def check_staff_role(self, role_id: int) -> bool:
532        """Check if the staff role exists.
533
534        A more efficient way to check if a staff role exists.
535        Instead of attempting to create the staff role,
536        we just check if it exists.
537
538        Args:
539            role_id: The role ID.
540
541        Returns:
542            bool: A boolean indicating if the staff role exists.
543        """
544        staff_role = await self._session.get(models.StaffRole, role_id)
545        return staff_role is not None

Check if the staff role exists.

A more efficient way to check if a staff role exists. Instead of attempting to create the staff role, we just check if it exists.

Arguments:
  • role_id: The role ID.
Returns:

bool: A boolean indicating if the staff role exists.

async def get_observers_role( self, role_id: int, guild_id: int) -> Tuple[bool, tickets_plus.database.models.ObserversRole]:
547    async def get_observers_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.ObserversRole]:
548        """Get or create the observer role from the database.
549
550        Fetches an observer role from the database.
551        If the observer role does not exist, it will be created.
552        We also check if the guild exists and create it if it does not.
553
554        Args:
555            role_id: The role ID.
556            guild_id: The guild ID.
557
558        Returns:
559            Tuple[bool, models.ObserversRole]: A tuple containing a boolean
560                indicating if the observer role was created, and the observers'
561                role. Relationships are loaded.
562        """
563        guild = await self.get_guild(guild_id)
564        observers_role = await self._session.get(models.ObserversRole, role_id)
565        new = False
566        if observers_role is None:
567            new = True
568            observers_role = models.ObserversRole(role_id=role_id, guild=guild)
569            self._session.add(observers_role)
570        return new, observers_role

Get or create the observer role from the database.

Fetches an observer role from the database. If the observer role does not exist, it will be created. We also check if the guild exists and create it if it does not.

Arguments:
  • role_id: The role ID.
  • guild_id: The guild ID.
Returns:

Tuple[bool, models.ObserversRole]: A tuple containing a boolean indicating if the observer role was created, and the observers' role. Relationships are loaded.

async def get_all_observers_roles( self, guild_id: int) -> Sequence[tickets_plus.database.models.ObserversRole]:
572    async def get_all_observers_roles(self, guild_id: int) -> Sequence[models.ObserversRole]:
573        """Get all observer roles from the database.
574
575        Fetches all observer roles from the database.
576        If the observer roles do not exist, an empty list is returned.
577
578        Args:
579            guild_id: The guild ID.
580
581        Returns:
582            Sequence[models.ObserversRole]: A list of observer roles.
583                Relationships are loaded.
584        """
585        guild = await self.get_guild(guild_id)
586        observers_roles = await self._session.scalars(
587            sql.select(models.ObserversRole).where(models.ObserversRole.guild == guild))
588        return observers_roles.all()

Get all observer roles from the database.

Fetches all observer roles from the database. If the observer roles do not exist, an empty list is returned.

Arguments:
  • guild_id: The guild ID.
Returns:

Sequence[models.ObserversRole]: A list of observer roles. Relationships are loaded.

async def check_observers_role(self, role_id: int) -> bool:
590    async def check_observers_role(self, role_id: int) -> bool:
591        """Check if the observer role exists.
592
593        A more efficient way to check if an observer role exists.
594        Instead of attempting to create the observer role,
595        we just check if it exists.
596
597        Args:
598            role_id: The role ID.
599
600        Returns:
601            bool: A boolean indicating if the observer role exists.
602        """
603        observers_role = await self._session.get(models.ObserversRole, role_id)
604        return observers_role is not None

Check if the observer role exists.

A more efficient way to check if an observer role exists. Instead of attempting to create the observer role, we just check if it exists.

Arguments:
  • role_id: The role ID.
Returns:

bool: A boolean indicating if the observer role exists.

async def get_community_role( self, role_id: int, guild_id: int) -> Tuple[bool, tickets_plus.database.models.CommunityRole]:
606    async def get_community_role(self, role_id: int, guild_id: int) -> Tuple[bool, models.CommunityRole]:
607        """Get or create the community role from the database.
608
609        Fetches a community role from the database.
610        If the community role does not exist, it will be created.
611        We also check if the guild exists and create it if it does not.
612
613        Args:
614            role_id: The role ID.
615            guild_id: The guild ID.
616
617        Returns:
618            Tuple[bool, models.CommunityRole]: A tuple containing a boolean
619                indicating if the community role was created, and the community
620                role. Relationships are loaded.
621        """
622        guild = await self.get_guild(guild_id)
623        community_role = await self._session.get(models.CommunityRole, role_id)
624        new = False
625        if community_role is None:
626            new = True
627            community_role = models.CommunityRole(role_id=role_id, guild=guild)
628            self._session.add(community_role)
629        return new, community_role

Get or create the community role from the database.

Fetches a community role from the database. If the community role does not exist, it will be created. We also check if the guild exists and create it if it does not.

Arguments:
  • role_id: The role ID.
  • guild_id: The guild ID.
Returns:

Tuple[bool, models.CommunityRole]: A tuple containing a boolean indicating if the community role was created, and the community role. Relationships are loaded.

async def get_all_community_roles( self, guild_id: int) -> Sequence[tickets_plus.database.models.CommunityRole]:
631    async def get_all_community_roles(self, guild_id: int) -> Sequence[models.CommunityRole]:
632        """Get all community roles from the database.
633
634        Fetches all community roles from the database.
635        If the community roles do not exist, an empty list is returned.
636
637        Args:
638            guild_id: The guild ID.
639
640        Returns:
641            Sequence[models.CommunityRole]: A list of community roles.
642                Relationships are loaded.
643        """
644        guild = await self.get_guild(guild_id)
645        community_roles = await self._session.scalars(
646            sql.select(models.CommunityRole).where(models.CommunityRole.guild == guild))
647        return community_roles.all()

Get all community roles from the database.

Fetches all community roles from the database. If the community roles do not exist, an empty list is returned.

Arguments:
  • guild_id: The guild ID.
Returns:

Sequence[models.CommunityRole]: A list of community roles. Relationships are loaded.

async def check_community_role(self, role_id: int) -> bool:
649    async def check_community_role(self, role_id: int) -> bool:
650        """Check if the community role exists.
651
652        A more efficient way to check if a community role exists.
653        Instead of attempting to create the community role,
654        we just check if it exists.
655
656        Args:
657            role_id: The role ID.
658
659        Returns:
660            bool: A boolean indicating if the community role exists.
661        """
662        community_role = await self._session.get(models.CommunityRole, role_id)
663        return community_role is not None

Check if the community role exists.

A more efficient way to check if a community role exists. Instead of attempting to create the community role, we just check if it exists.

Arguments:
  • role_id: The role ID.
Returns:

bool: A boolean indicating if the community role exists.

async def get_community_ping( self, role_id: int, guild_id: int) -> Tuple[bool, tickets_plus.database.models.CommunityPing]:
665    async def get_community_ping(self, role_id: int, guild_id: int) -> Tuple[bool, models.CommunityPing]:
666        """Get or create the community ping from the database.
667
668        Fetches a community ping from the database.
669        If the community ping does not exist, it will be created.
670        We also check if the guild exists and create it if it does not.
671
672        Args:
673            role_id: The role ID.
674            guild_id: The guild ID.
675
676        Returns:
677            Tuple[bool, models.CommunityPing]: A tuple containing a boolean
678                indicating if the community ping was created, and the community
679                pings. Relationships are loaded.
680        """
681        guild = await self.get_guild(guild_id)
682        community_ping = await self._session.get(models.CommunityPing, role_id)
683        new = False
684        if community_ping is None:
685            new = True
686            community_ping = models.CommunityPing(role_id=role_id, guild=guild)
687            self._session.add(community_ping)
688        return new, community_ping

Get or create the community ping from the database.

Fetches a community ping from the database. If the community ping does not exist, it will be created. We also check if the guild exists and create it if it does not.

Arguments:
  • role_id: The role ID.
  • guild_id: The guild ID.
Returns:

Tuple[bool, models.CommunityPing]: A tuple containing a boolean indicating if the community ping was created, and the community pings. Relationships are loaded.

async def get_all_community_pings( self, guild_id: int) -> Sequence[tickets_plus.database.models.CommunityPing]:
690    async def get_all_community_pings(self, guild_id: int) -> Sequence[models.CommunityPing]:
691        """Get all community pings from the database.
692
693        Fetches all community pings from the database.
694        If the community pings do not exist, an empty list is returned.
695
696        Args:
697            guild_id: The guild ID.
698
699        Returns:
700            Sequence[models.CommunityPing]: A list of community pings.
701                Relationships are loaded.
702        """
703        guild = await self.get_guild(guild_id)
704        community_pings = await self._session.scalars(
705            sql.select(models.CommunityPing).where(models.CommunityPing.guild == guild))
706        return community_pings.all()

Get all community pings from the database.

Fetches all community pings from the database. If the community pings do not exist, an empty list is returned.

Arguments:
  • guild_id: The guild ID.
Returns:

Sequence[models.CommunityPing]: A list of community pings. Relationships are loaded.

async def check_community_ping(self, role_id: int) -> bool:
708    async def check_community_ping(self, role_id: int) -> bool:
709        """Check if the community ping exists.
710
711        A more efficient way to check if a community ping exists.
712        Instead of attempting to create the community ping,
713        we just check if it exists.
714
715        Args:
716            role_id: The role ID.
717
718        Returns:
719            bool: A boolean indicating if the community ping exists.
720        """
721        community_ping = await self._session.get(models.CommunityPing, role_id)
722        return community_ping is not None

Check if the community ping exists.

A more efficient way to check if a community ping exists. Instead of attempting to create the community ping, we just check if it exists.

Arguments:
  • role_id: The role ID.
Returns:

bool: A boolean indicating if the community ping exists.