Implement an Asynchronous Task Management system using OOP principles #371
Comments
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
All progress on this addition are in the |
I've made some large revisions on the |
ImplementationIn order to implement this module, we will need these things:
|
1. Job SubmoduleThis module defines the basis for all jobs. The pattern of inheritance for jobs is as follows:
There are multiple utility jobs based upon these base job classes to ease things like Discord messaging, scheduling a method call, and more. Various methods inherited from Every job object can use its
|
2. Job Manager SubmoduleThis module defines the job manager.
Job objects are unable to run properly if they are not added to a |
3 & 4. Event Classes SubmoduleThese modules define base classes for event objects, which are used to propagate event occurences to all jobs that are listening for them. They can also store data in attributes to pass on to listening jobs.
|
ExamplesThis sample code shows how the main file for job-class-based program is meant to be structured. Here, the
class GreetingTest(core.ClientEventJobBase, permission_level=JobPermissionLevels.MEDIUM): # MEDIUM is the default
"""
A job that waits for a user to type a message starting with 'hi', before responding with 'Hi, what's your name?'.
This job will then wait until it receives another `OnMessage` event, before saying 'Hi, {event_content}'
"""
EVENT_TYPES = (events.OnMessage,)
def __init__(self, target_channel: Optional[discord.TextChannel] = None):
super().__init__() # very important
self.data.target_channel = target_channel
async def on_init(self):
if self.data.target_channel is None:
self.data.target_channel = common.guild.get_channel(822650791303053342)
def check_event(self, event: events.OnMessage): # additionally validate any dispatched events
return event.message.channel.id == self.data.target_channel.id
async def on_run(self, event: events.OnMessage):
if event.message.content.lower().startswith("hi"):
with self.queue_blocker(): # block the event queue of this job while talking to a user, so that other events are ignored if intended
await self.data.target_channel.send("Hi, what's your name?")
author = event.message.author
check = (
lambda x: x.message.author == author
and x.message.channel == self.data.target_channel
and x.message.content
)
name_event = await self.wait_for(self.manager.wait_for_event( # self.wait_for signals that the job is awaiting something to other jobs and the job manager
events.OnMessage, check=check
))
user_name = name_event.message.content
await self.data.target_channel.send(f"Hi, {user_name}")
class Main(core.SingleRunJob, permission_level=JobPermissionLevel.HIGHEST): # prevent most jobs from killing the `Main` job
"""The main job class that serves as an entry into a program that uses jobs.
"""
async def on_run(self):
await self.manager.create_and_register_job(GreetingTest)
__all__ = [
"Main",
] |
This issue will now be closed, as the code for implementing this functionality has been made bot-agnostic and is awaiting addition to the early |
Is your feature request related to a problem? Please describe.
Our current way of running asynchronous tasks that run in the background (e.g. bot reminders) is very limited. For instance, it isn't currently possible to dispatch a discord API event to several task functions at once. We also have no control of them at runtime, which could be very important for scenarios where a task function should end prematurely, either due to bugs or other server-specific reasons.
Describe the solution you'd like
A
jobs
module that defines a set of base classes which can be subclassed in order to implement specific types of job classes (interval based, event based, etc.) in the server for the bot to run. These classes would produce independent job instances as objects, which run based on specific data given as input. All active jobs would be managed by a job manager object which keeps track of them and can view, modify, pause, stop, restart or kill jobs at runtime. It should also be responsible for dispatching discord API events to the job types that support them. Job objects should also be able to interact with other jobs in different ways, like allowing other jobs to be notified when one job is finished/killed, or when it has produced a specific type of output. There would also need to be a permission system that prevents some tasks from stopping/killing other ones at runtime.Describe alternatives you've considered
In the end, one could argue that all of this can be implemented using a simple
discord.ext.tasks.loop
decorator bound to a function and other data structures, but this alone is very inflexible, and misses out on the many possibilities that an OOP-based task system offers.The text was updated successfully, but these errors were encountered: