Improved scheduled_event handling #410
Some checks reported warnings
Deploy dev on push / on-push-deploy_sh-edraft (push) Has been cancelled
Some checks reported warnings
Deploy dev on push / on-push-deploy_sh-edraft (push) Has been cancelled
This commit is contained in:
parent
090f217f93
commit
0d3db75190
@ -7,6 +7,7 @@ from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
|
|||||||
from bot_core.abc.module_abc import ModuleABC
|
from bot_core.abc.module_abc import ModuleABC
|
||||||
from bot_core.abc.task_abc import TaskABC
|
from bot_core.abc.task_abc import TaskABC
|
||||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
|
from modules.base.command.scheduled_events_group import ScheduledEventsCommand
|
||||||
from modules.base.tasks.birthday_watcher import BirthdayWatcher
|
from modules.base.tasks.birthday_watcher import BirthdayWatcher
|
||||||
from modules.base.command.afk_command import AFKCommand
|
from modules.base.command.afk_command import AFKCommand
|
||||||
from modules.base.command.game_server_group import GameServerGroup
|
from modules.base.command.game_server_group import GameServerGroup
|
||||||
@ -83,6 +84,7 @@ class BaseModule(ModuleABC):
|
|||||||
services.add_transient(RegisterGroup)
|
services.add_transient(RegisterGroup)
|
||||||
services.add_transient(UnregisterGroup)
|
services.add_transient(UnregisterGroup)
|
||||||
services.add_transient(GameServerGroup)
|
services.add_transient(GameServerGroup)
|
||||||
|
services.add_transient(ScheduledEventsCommand)
|
||||||
# events
|
# events
|
||||||
services.add_transient(DiscordEventTypesEnum.on_command.value, BaseOnCommandEvent)
|
services.add_transient(DiscordEventTypesEnum.on_command.value, BaseOnCommandEvent)
|
||||||
services.add_transient(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent)
|
services.add_transient(DiscordEventTypesEnum.on_command_error.value, BaseOnCommandErrorEvent)
|
||||||
|
37
bot/src/modules/base/command/scheduled_events_group.py
Normal file
37
bot/src/modules/base/command/scheduled_events_group.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from cpl_discord.command import DiscordCommandABC
|
||||||
|
from discord.ext import commands
|
||||||
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
from bot_core.helper.command_checks import CommandChecks
|
||||||
|
from bot_core.logging.command_logger import CommandLogger
|
||||||
|
from modules.base.service.event_service import EventService
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduledEventsCommand(DiscordCommandABC):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
logger: CommandLogger,
|
||||||
|
events: EventService,
|
||||||
|
):
|
||||||
|
DiscordCommandABC.__init__(self)
|
||||||
|
|
||||||
|
self._logger = logger
|
||||||
|
self._events = events
|
||||||
|
|
||||||
|
self._logger.trace(__name__, f"Loaded command service: {type(self).__name__}")
|
||||||
|
|
||||||
|
@commands.group(name="scheduled-events")
|
||||||
|
@commands.guild_only()
|
||||||
|
async def scheduled_events(self, ctx: Context):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@scheduled_events.command()
|
||||||
|
@commands.guild_only()
|
||||||
|
@CommandChecks.check_is_ready()
|
||||||
|
@CommandChecks.check_is_member_moderator()
|
||||||
|
async def reload(self, ctx: Context):
|
||||||
|
self._logger.debug(__name__, "Running scheduled-events reload")
|
||||||
|
try:
|
||||||
|
await self._events.check_and_create_scheduled_events(ctx.guild)
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f"Reloading scheduled events failed", e)
|
@ -41,5 +41,6 @@ class BaseOnScheduledEventUpdateEvent(OnScheduledEventUpdateABC):
|
|||||||
if event is None:
|
if event is None:
|
||||||
return
|
return
|
||||||
self._events.remove_event(event)
|
self._events.remove_event(event)
|
||||||
|
await self._events.check_and_create_scheduled_events(before.guild)
|
||||||
|
|
||||||
self._logger.debug(__name__, f"Module {type(self)} stopped")
|
self._logger.debug(__name__, f"Module {type(self)} stopped")
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
|
import calendar
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from cpl_core.configuration import ConfigurationABC
|
from cpl_core.configuration import ConfigurationABC
|
||||||
from cpl_core.database.context import DatabaseContextABC
|
from cpl_core.database.context import DatabaseContextABC
|
||||||
from cpl_core.logging import LoggerABC
|
from cpl_core.logging import LoggerABC
|
||||||
|
from cpl_discord.container import Guild
|
||||||
from cpl_query.extension import List
|
from cpl_query.extension import List
|
||||||
|
from discord import PrivacyLevel
|
||||||
|
from discord.scheduled_event import ScheduledEvent as DiscordEvent
|
||||||
|
|
||||||
|
from bot_data.abc.scheduled_event_repository_abc import ScheduledEventRepositoryABC
|
||||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||||
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
from bot_data.abc.user_repository_abc import UserRepositoryABC
|
||||||
|
from bot_data.model.scheduled_event import ScheduledEvent
|
||||||
|
from bot_data.model.scheduled_event_interval_enum import ScheduledEventIntervalEnum
|
||||||
from bot_data.model.server_config import ServerConfig
|
from bot_data.model.server_config import ServerConfig
|
||||||
from modules.base.model.active_event import ActiveEvent
|
from modules.base.model.active_event import ActiveEvent
|
||||||
|
|
||||||
@ -20,12 +29,14 @@ class EventService:
|
|||||||
servers: ServerRepositoryABC,
|
servers: ServerRepositoryABC,
|
||||||
users: UserRepositoryABC,
|
users: UserRepositoryABC,
|
||||||
db: DatabaseContextABC,
|
db: DatabaseContextABC,
|
||||||
|
events: ScheduledEventRepositoryABC,
|
||||||
):
|
):
|
||||||
self._config = config
|
self._config = config
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
self._servers = servers
|
self._servers = servers
|
||||||
self._users = users
|
self._users = users
|
||||||
self._db = db
|
self._db = db
|
||||||
|
self._events = events
|
||||||
|
|
||||||
self._active_events = List(ActiveEvent)
|
self._active_events = List(ActiveEvent)
|
||||||
|
|
||||||
@ -61,3 +72,69 @@ class EventService:
|
|||||||
self._users.update_user(user)
|
self._users.update_user(user)
|
||||||
self._db.save_changes()
|
self._db.save_changes()
|
||||||
active_event.participants.append(user)
|
active_event.participants.append(user)
|
||||||
|
|
||||||
|
def _append_interval(self, interval: ScheduledEventIntervalEnum, ts: datetime) -> datetime:
|
||||||
|
if ts >= datetime.now():
|
||||||
|
return ts
|
||||||
|
|
||||||
|
if interval == ScheduledEventIntervalEnum.daily:
|
||||||
|
ts = ts + timedelta(days=1)
|
||||||
|
|
||||||
|
elif interval == ScheduledEventIntervalEnum.weekly:
|
||||||
|
ts = ts + timedelta(weeks=1)
|
||||||
|
|
||||||
|
elif interval == ScheduledEventIntervalEnum.monthly:
|
||||||
|
days_in_month = calendar.monthrange(ts.year, ts.month + 1)[1]
|
||||||
|
ts = ts + timedelta(days=days_in_month)
|
||||||
|
|
||||||
|
elif interval == ScheduledEventIntervalEnum.yearly:
|
||||||
|
ts = ts + timedelta(days=365)
|
||||||
|
|
||||||
|
while ts < datetime.now():
|
||||||
|
ts = self._append_interval(interval, ts)
|
||||||
|
|
||||||
|
return ts
|
||||||
|
|
||||||
|
async def check_and_create_scheduled_events(self, guild: Guild):
|
||||||
|
server = self._servers.get_server_by_discord_id(guild.id)
|
||||||
|
scheduled_events_from_db = self._events.get_scheduled_events_by_server_id(server.id)
|
||||||
|
for scheduled_event in scheduled_events_from_db:
|
||||||
|
scheduled_event: ScheduledEvent = scheduled_event
|
||||||
|
from_guild = List(DiscordEvent, guild.scheduled_events).where(
|
||||||
|
lambda x: x.name == scheduled_event.name
|
||||||
|
and x.description == scheduled_event.description
|
||||||
|
and x.entity_type == scheduled_event.entity_type
|
||||||
|
)
|
||||||
|
if from_guild.count() != 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
kwargs = {"name": scheduled_event.name, "description": scheduled_event.description}
|
||||||
|
|
||||||
|
if scheduled_event.channel_id is not None:
|
||||||
|
kwargs["channel"] = guild.get_channel(scheduled_event.channel_id)
|
||||||
|
|
||||||
|
if scheduled_event.start_time is not None:
|
||||||
|
scheduled_event.start_time = self._append_interval(scheduled_event.interval, scheduled_event.start_time)
|
||||||
|
|
||||||
|
start_time = scheduled_event.start_time.replace(tzinfo=ZoneInfo("Europe/Berlin"))
|
||||||
|
|
||||||
|
kwargs["start_time"] = start_time
|
||||||
|
|
||||||
|
if scheduled_event.end_time is not None:
|
||||||
|
scheduled_event.end_time = self._append_interval(scheduled_event.interval, scheduled_event.end_time)
|
||||||
|
end_time = scheduled_event.end_time.replace(tzinfo=ZoneInfo("Europe/Berlin"))
|
||||||
|
kwargs["end_time"] = end_time
|
||||||
|
|
||||||
|
kwargs["entity_type"] = scheduled_event.entity_type
|
||||||
|
if scheduled_event.location is not None:
|
||||||
|
kwargs["location"] = scheduled_event.location
|
||||||
|
|
||||||
|
kwargs["privacy_level"] = PrivacyLevel.guild_only
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._logger.debug(__name__, f"Try to create scheduled event for guild {guild.name}")
|
||||||
|
await guild.create_scheduled_event(**kwargs)
|
||||||
|
self._events.update_scheduled_event(scheduled_event)
|
||||||
|
self._db.save_changes()
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.error(__name__, f"Watching scheduled events failed", e)
|
||||||
|
@ -1,23 +1,14 @@
|
|||||||
import calendar
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from zoneinfo import ZoneInfo
|
|
||||||
|
|
||||||
from cpl_core.configuration import ConfigurationABC
|
from cpl_core.configuration import ConfigurationABC
|
||||||
from cpl_core.database.context import DatabaseContextABC
|
from cpl_core.database.context import DatabaseContextABC
|
||||||
from cpl_discord.service import DiscordBotServiceABC
|
from cpl_discord.service import DiscordBotServiceABC
|
||||||
from cpl_query.extension import List
|
|
||||||
from cpl_translation import TranslatePipe
|
from cpl_translation import TranslatePipe
|
||||||
from discord import Guild, PrivacyLevel
|
|
||||||
from discord.ext import tasks
|
from discord.ext import tasks
|
||||||
from discord.scheduled_event import ScheduledEvent as DiscordEvent
|
|
||||||
|
|
||||||
from bot_core.abc.task_abc import TaskABC
|
from bot_core.abc.task_abc import TaskABC
|
||||||
from bot_core.logging.task_logger import TaskLogger
|
from bot_core.logging.task_logger import TaskLogger
|
||||||
from bot_core.service.message_service import MessageService
|
from bot_core.service.message_service import MessageService
|
||||||
from bot_data.abc.scheduled_event_repository_abc import ScheduledEventRepositoryABC
|
|
||||||
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
from bot_data.abc.server_repository_abc import ServerRepositoryABC
|
||||||
from bot_data.model.scheduled_event import ScheduledEvent
|
from modules.base.service.event_service import EventService
|
||||||
from bot_data.model.scheduled_event_interval_enum import ScheduledEventIntervalEnum
|
|
||||||
|
|
||||||
|
|
||||||
class ScheduledEventsWatcher(TaskABC):
|
class ScheduledEventsWatcher(TaskABC):
|
||||||
@ -28,7 +19,7 @@ class ScheduledEventsWatcher(TaskABC):
|
|||||||
bot: DiscordBotServiceABC,
|
bot: DiscordBotServiceABC,
|
||||||
db: DatabaseContextABC,
|
db: DatabaseContextABC,
|
||||||
servers: ServerRepositoryABC,
|
servers: ServerRepositoryABC,
|
||||||
events: ScheduledEventRepositoryABC,
|
events: EventService,
|
||||||
message_service: MessageService,
|
message_service: MessageService,
|
||||||
t: TranslatePipe,
|
t: TranslatePipe,
|
||||||
):
|
):
|
||||||
@ -46,80 +37,12 @@ class ScheduledEventsWatcher(TaskABC):
|
|||||||
if not self._is_maintenance():
|
if not self._is_maintenance():
|
||||||
self.watch.start()
|
self.watch.start()
|
||||||
|
|
||||||
def _append_interval(self, interval: ScheduledEventIntervalEnum, ts: datetime) -> datetime:
|
@tasks.loop(hours=12)
|
||||||
now = datetime.now()
|
|
||||||
if ts >= now:
|
|
||||||
return ts
|
|
||||||
|
|
||||||
if interval == ScheduledEventIntervalEnum.daily:
|
|
||||||
ts = ts + timedelta(days=1)
|
|
||||||
|
|
||||||
elif interval == ScheduledEventIntervalEnum.weekly:
|
|
||||||
ts = ts + timedelta(weeks=1)
|
|
||||||
|
|
||||||
elif interval == ScheduledEventIntervalEnum.monthly:
|
|
||||||
days_in_month = calendar.monthrange(ts.year, ts.month + 1)[1]
|
|
||||||
ts = ts + timedelta(days=days_in_month)
|
|
||||||
|
|
||||||
elif interval == ScheduledEventIntervalEnum.yearly:
|
|
||||||
ts = ts + timedelta(days=365)
|
|
||||||
|
|
||||||
if ts < now:
|
|
||||||
return self._append_interval(interval, ts)
|
|
||||||
return ts
|
|
||||||
|
|
||||||
@tasks.loop(hours=24)
|
|
||||||
async def watch(self):
|
async def watch(self):
|
||||||
self._logger.info(__name__, "Watching scheduled events")
|
self._logger.info(__name__, "Watching scheduled events")
|
||||||
try:
|
try:
|
||||||
for guild in self._bot.guilds:
|
for guild in self._bot.guilds:
|
||||||
guild: Guild = guild
|
await self._events.check_and_create_scheduled_events(guild)
|
||||||
server = self._servers.get_server_by_discord_id(guild.id)
|
|
||||||
scheduled_events_from_guild = self._events.get_scheduled_events_by_server_id(server.id)
|
|
||||||
for scheduled_event in scheduled_events_from_guild:
|
|
||||||
scheduled_event: ScheduledEvent = scheduled_event
|
|
||||||
from_guild = List(DiscordEvent, guild.scheduled_events).where(
|
|
||||||
lambda x: x.name == scheduled_event.name
|
|
||||||
and x.description == scheduled_event.description
|
|
||||||
and x.entity_type == scheduled_event.entity_type
|
|
||||||
)
|
|
||||||
if from_guild.count() != 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
kwargs = {"name": scheduled_event.name, "description": scheduled_event.description}
|
|
||||||
|
|
||||||
if scheduled_event.channel_id is not None:
|
|
||||||
kwargs["channel"] = guild.get_channel(scheduled_event.channel_id)
|
|
||||||
|
|
||||||
if scheduled_event.start_time is not None:
|
|
||||||
scheduled_event.start_time = self._append_interval(
|
|
||||||
scheduled_event.interval, scheduled_event.start_time
|
|
||||||
)
|
|
||||||
|
|
||||||
start_time = scheduled_event.start_time.replace(tzinfo=ZoneInfo("Europe/Berlin"))
|
|
||||||
|
|
||||||
kwargs["start_time"] = start_time
|
|
||||||
|
|
||||||
if scheduled_event.end_time is not None:
|
|
||||||
scheduled_event.end_time = self._append_interval(
|
|
||||||
scheduled_event.interval, scheduled_event.end_time
|
|
||||||
)
|
|
||||||
end_time = scheduled_event.end_time.replace(tzinfo=ZoneInfo("Europe/Berlin"))
|
|
||||||
kwargs["end_time"] = end_time
|
|
||||||
|
|
||||||
kwargs["entity_type"] = scheduled_event.entity_type
|
|
||||||
if scheduled_event.location is not None:
|
|
||||||
kwargs["location"] = scheduled_event.location
|
|
||||||
|
|
||||||
kwargs["privacy_level"] = PrivacyLevel.guild_only
|
|
||||||
|
|
||||||
try:
|
|
||||||
self._logger.debug(__name__, f"Try to create scheduled event for guild {guild.name}")
|
|
||||||
await guild.create_scheduled_event(**kwargs)
|
|
||||||
self._events.update_scheduled_event(scheduled_event)
|
|
||||||
self._db.save_changes()
|
|
||||||
except Exception as e:
|
|
||||||
self._logger.error(__name__, f"Watching scheduled events failed", e)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._logger.error(__name__, f"Watching scheduled events failed", e)
|
self._logger.error(__name__, f"Watching scheduled events failed", e)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user