Improved achievement logic #268_achievements

This commit is contained in:
2023-07-13 13:54:13 +02:00
parent 2578c47c44
commit e36f0b8c76
17 changed files with 375 additions and 112 deletions

View File

@@ -0,0 +1,78 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
from bot_core.configuration.server_settings import ServerSettings
from bot_core.service.message_service import MessageService
from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC
from bot_data.model.achievement import Achievement
from bot_data.model.user import User
from bot_data.model.user_got_achievement import UserGotAchievement
class AchievementService:
def __init__(
self,
config: ConfigurationABC,
logger: LoggerABC,
bot: DiscordBotServiceABC,
achievements: AchievementRepositoryABC,
db: DatabaseContextABC,
message_service: MessageService,
t: TranslatePipe,
):
self._config = config
self._logger = logger
self._bot = bot
self._achievements = achievements
self._db = db
self._message_service = message_service
self._t = t
def _match(self, value: str, operator: str, expected_value: str) -> bool:
match operator:
case "==":
return value == expected_value
case "!=":
return value != expected_value
case "<=":
return value <= expected_value
case ">=":
return value >= expected_value
case "<":
return value < expected_value
case ">":
return value > expected_value
case _:
raise ValueError(f"Invalid operator: ${operator}")
def has_user_achievement_already(self, user: User, achievement: Achievement) -> bool:
user_achievements = self._achievements.get_achievements_by_user_id(user.id)
return user_achievements.where(lambda x: x.name == achievement.name).count() > 0
def has_user_achievement(self, user: User, achievement: Achievement) -> bool:
return self._match(str(getattr(user, achievement.attribute)), achievement.operator, achievement.value)
async def validate_achievements_for_user(self, user: User):
achievements = self._achievements.get_achievements_by_server_id(user.server.id)
for achievement in achievements:
if self.has_user_achievement_already(user, achievement) and not self.has_user_achievement(
user, achievement
):
continue
self._achievements.add_user_got_achievement(UserGotAchievement(user, achievement, user.server))
self._db.save_changes()
await self._send_achievement_notification(user.server.discord_id, user.discord_id, achievement.name)
async def _send_achievement_notification(self, guild_id: int, member_id: int, achievement_name: str):
member = self._bot.get_guild(guild_id).get_member(member_id)
settings: ServerSettings = self._config.get_configuration(f"ServerSettings_{guild_id}")
await self._message_service.send_channel_message(
self._bot.get_channel(settings.notification_chat_id),
self._t.transform("modules.achievements.got_new_achievement").format(member.mention, achievement_name),
is_persistent=True,
)

View File

@@ -1,10 +1,13 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_discord.discord_event_types_enum import DiscordEventTypesEnum
from cpl_discord.service.discord_collection_abc import DiscordCollectionABC
from bot_core.abc.module_abc import ModuleABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from modules.achievements.achievement_service import AchievementService
from modules.achievements.events.achievement_on_message_event import AchievementOnMessageEvent
class AchievementsModule(ModuleABC):
@@ -15,4 +18,5 @@ class AchievementsModule(ModuleABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
pass
services.add_transient(AchievementService)
self._dc.add_event(DiscordEventTypesEnum.on_message.value, AchievementOnMessageEvent)

View File

@@ -0,0 +1 @@
# imports

View File

@@ -0,0 +1,43 @@
import discord
from cpl_core.database.context import DatabaseContextABC
from cpl_core.logging import LoggerABC
from cpl_discord.events import OnMessageABC
from cpl_discord.service import DiscordBotServiceABC
from bot_core.helper.event_checks import EventChecks
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from modules.achievements.achievement_service import AchievementService
class AchievementOnMessageEvent(OnMessageABC):
def __init__(
self,
logger: LoggerABC,
bot: DiscordBotServiceABC,
achievements: AchievementService,
db: DatabaseContextABC,
servers: ServerRepositoryABC,
users: UserRepositoryABC,
):
OnMessageABC.__init__(self)
self._logger = logger
self._bot = bot
self._achievements = achievements
self._db = db
self._servers = servers
self._users = users
@EventChecks.check_is_ready()
async def on_message(self, message: discord.Message):
if message.author.bot:
return
server = self._servers.get_server_by_discord_id(message.guild.id)
user = self._users.get_user_by_discord_id_and_server_id(message.author.id, server.id)
user.message_count += 1
self._db.save_changes()
await self._achievements.validate_achievements_for_user(user)

View File

@@ -1,28 +0,0 @@
import traceback
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console
class LevelServerSettings(ConfigurationModelABC):
def __init__(self):
ConfigurationModelABC.__init__(self)
self._id: int = 0
self._changed_level_notification_channel = 0
@property
def id(self) -> int:
return self._id
@property
def changed_level_notification_channel(self) -> int:
return self._changed_level_notification_channel
def from_dict(self, settings: dict):
try:
self._id = int(settings["Id"])
self._changed_level_notification_channel = int(settings["ChangedLevelNotificationChannelId"])
except Exception as e:
Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -1,31 +0,0 @@
import traceback
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console
from cpl_query.extension import List
from modules.level.configuration.level_server_settings import LevelServerSettings
class LevelSettings(ConfigurationModelABC):
def __init__(self):
ConfigurationModelABC.__init__(self)
self._servers: List[LevelServerSettings] = List()
@property
def servers(self) -> List[LevelServerSettings]:
return self._servers
def from_dict(self, settings: dict):
try:
servers = List(LevelServerSettings)
for s in settings:
st = LevelServerSettings()
settings[s]["Id"] = s
st.from_dict(settings[s])
servers.append(st)
self._servers = servers
except Exception as e:
Console.error(f"[ ERROR ] [ {__name__} ]: Reading error in {type(self).__name__} settings")
Console.error(f"[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}")

View File

@@ -6,13 +6,13 @@ from cpl_discord.container import Guild, Role, Member
from cpl_discord.service import DiscordBotServiceABC
from cpl_translation import TranslatePipe
from bot_core.configuration.server_settings import ServerSettings
from bot_core.service.message_service import MessageService
from bot_data.model.level import Level
from bot_data.model.user import User
from bot_data.service.level_repository_service import LevelRepositoryService
from bot_data.service.server_repository_service import ServerRepositoryService
from bot_data.service.user_repository_service import UserRepositoryService
from modules.level.configuration.level_server_settings import LevelServerSettings
class LevelService:
@@ -75,9 +75,9 @@ class LevelService:
self._logger.error(__name__, f"Adding role {level_role.name} to {member.name} failed!", e)
if notification_needed:
level_settings: LevelServerSettings = self._config.get_configuration(f"LevelServerSettings_{guild.id}")
settings: ServerSettings = self._config.get_configuration(f"ServerSettings_{guild.id}")
await self._message_service.send_channel_message(
self._bot.get_channel(level_settings.changed_level_notification_channel),
self._bot.get_channel(settings.notification_chat_id),
self._t.transform("modules.level.new_level_message").format(member.mention, level.name),
is_persistent=True,
)