diff --git a/core/igncore.py b/core/igncore.py
index c3ba636..a3b258e 100644
--- a/core/igncore.py
+++ b/core/igncore.py
@@ -48,7 +48,7 @@ class IgnCore:
self.last_timer_event = 0
self.start_time = int(time.time())
self.major_version = "IGNCore v2.9"
- self.minor_version = "7"
+ self.minor_version = "8"
self.incoming_queue = FifoQueue()
self.mass_message_queue = None
self.conns = DictObject()
diff --git a/modules/core/accounting/account_controller.py b/modules/core/accounting/account_controller.py
index 9a428e3..c81e139 100644
--- a/modules/core/accounting/account_controller.py
+++ b/modules/core/accounting/account_controller.py
@@ -136,7 +136,7 @@ class AccountController:
self.account_service.add_log(user.char_id, "system",
f"Account disabled by {request.sender.name}.",
request.sender.char_id)
- return f"Character {user.name}'s Account has been disabled!"
+ return f"Character {user.name}'s Account has been disabled permanently!"
else:
return f"There's no active account for character {user.name}."
diff --git a/modules/core/ban/ban_controller.py b/modules/core/ban/ban_controller.py
index 219a71c..718a0a2 100644
--- a/modules/core/ban/ban_controller.py
+++ b/modules/core/ban/ban_controller.py
@@ -1,14 +1,21 @@
+import time
+import typing
+
import hjson
from core.aochat.BaseModule import BaseModule
from core.chat_blob import ChatBlob
from core.command_param_types import Any, Const, Options, Time, Character
from core.db import DB
-from core.decorators import instance, command
-from core.lookup.character_service import CharacterService
-from core.translation_service import TranslationService
+from core.decorators import instance, command, timerevent
+
from core.igncore import IgnCore
-from modules.core.ban.ban_service import BanService
+
+if typing.TYPE_CHECKING:
+ from modules.core.accounting.services.account_service import AccountService
+ from modules.core.ban.ban_service import BanService
+ from core.lookup.character_service import CharacterService
+ from core.translation_service import TranslationService
@instance()
@@ -19,6 +26,7 @@ class BanController(BaseModule):
self.db: DB = registry.get_instance("db")
self.text = registry.get_instance("text")
self.util = registry.get_instance("util")
+ self.account_service: AccountService = registry.get_instance("account_service")
self.ban_service: BanService = registry.get_instance("ban_service")
self.command_alias_service = registry.get_instance("command_alias_service")
self.ts: TranslationService = registry.get_instance("translation_service")
@@ -36,12 +44,13 @@ class BanController(BaseModule):
@command(command="ban", params=[Const("list", is_optional=True)], access_level="moderator",
description="Show the ban list")
def ban_list_cmd(self, _, _1):
- query = self.db.query("SELECT a.*, p.*, b.reason, b.finished_at, b.created_at, b.sender_char_id from account a "
- "left join player p on a.char_id = p.char_id "
- "left join ban_list b on a.char_id = b.char_id "
- "where main=a.char_id "
- "and disabled=1 "
- "and p.org_id NOT IN (SELECT org_id from ban_org_list)")
+ # query = self.db.query("SELECT a.*, p.*, b.reason, b.finished_at, b.created_at, b.sender_char_id from account a "
+ # "left join player p on a.char_id = p.char_id "
+ # "left join ban_list b on a.char_id = b.char_id "
+ # "where main=a.char_id "
+ # "and (disabled=1 or b.ended_early < 1)"
+ # "and p.org_id NOT IN (SELECT org_id from ban_org_list)")
+ query = self.ban_service.get_ban_list()
blob = ""
for row in query:
ends = "never" if (row.finished_at or -1) == -1 else self.util.format_datetime(row.finished_at)
@@ -56,13 +65,19 @@ class BanController(BaseModule):
@command(command="ban", params=[Options(["rem", "remove"]), Character("character")], access_level="moderator",
description="Remove a character from the ban list")
- def ban_remove_cmd(self, _, _1, char):
+ def ban_remove_cmd(self, request, _1, char):
if not char.char_id:
return self.getresp("global", "char_not_found", {"char": char.name})
elif not self.ban_service.get_ban(char.char_id):
return self.getresp("module/ban", "not_banned", {"char": char.name})
else:
self.ban_service.remove_ban(char.char_id)
+ self.account_service.add_log(char.char_id, 'admin',
+ 'Ban has been lifted.',
+ request.sender.char_id)
+ self.account_service.add_log(request.sender.char_id, 'admin',
+ f'lifted active ban for {char.name}',
+ self.bot.get_char_id())
return self.getresp("module/ban", "unbanned_self", {"char": char.name})
@command(command="ban",
@@ -75,5 +90,33 @@ class BanController(BaseModule):
elif self.ban_service.get_ban(char.char_id):
return f"{char.name} is already banned."
else:
+ if not duration:
+ duration = -1
+ if not reason:
+ reason = 'No reason given.'
+ else:
+ reason = f"Reason: {reason}"
+ self.account_service.add_log(char.char_id, 'admin', f'{"Banned permanently. " if duration < 0 else f"Banned for {self.util.time_to_readable(duration)}."} {reason}', request.sender.char_id)
+ self.account_service.add_log(request.sender.char_id, 'admin',
+ f' {char.name} has been {"banned permanently. " if duration < 0 else f"banned for {self.util.time_to_readable(duration)}."} {reason}',
+ request.sender.char_id)
+
self.ban_service.add_ban(char.char_id, request.sender.char_id, duration, reason)
request.reply(f"{char.name} has been added to the ban list.")
+
+ @timerevent(budatime="1h", description="lift expired temporary bans")
+ def fix_bans(self, _, _1):
+ d = self.db.query(
+ f"SELECT b.*, COALESCE(p1.name, b.char_id) AS name, p2.name AS sender_name FROM ban_list b "
+ f"LEFT JOIN player p1 ON b.char_id = p1.char_id "
+ f"LEFT JOIN player p2 ON b.sender_char_id = p2.char_id "
+ f"WHERE ended_early < 1 AND (finished_at < ?) "
+ f"ORDER BY b.created_at DESC", [time.time()])
+
+ for active in d:
+ dur = time.time() - active.created_at
+ self.ban_service.remove_ban(active.char_id, True)
+ self.account_service.add_log(active.char_id, "admin", f"Temporary ban lifted after a duration of {self.util.time_to_readable(dur)}. {active.reason}", self.bot.get_char_id())
+ for still_active in self.ban_service.get_ban_list():
+ self.account_service.account_disable(still_active.char_id)
+
diff --git a/modules/core/ban/ban_service.py b/modules/core/ban/ban_service.py
index 77e51a7..b5f9a6a 100644
--- a/modules/core/ban/ban_service.py
+++ b/modules/core/ban/ban_service.py
@@ -67,10 +67,14 @@ class BanService:
return num_rows
- def remove_ban(self, char_id):
+ def remove_ban(self, char_id, tmp=False):
t = int(time.time())
- num_rows = self.db.exec("UPDATE ban_list SET ended_early = 1 "
- "WHERE char_id = ? AND (finished_at > ? OR finished_at = -1)", [char_id, t])
+ if not tmp:
+ num_rows = self.db.exec("UPDATE ban_list SET ended_early = 1 "
+ "WHERE char_id = ? and ended_early = 0", [char_id])
+ else:
+ num_rows = self.db.exec("UPDATE ban_list SET ended_early = 2 "
+ "WHERE char_id = ? AND (finished_at < ?)", [char_id, t])
if num_rows:
self.db.exec("UPDATE account SET disabled = 0 "
"WHERE main = (SELECT main from account where char_id=?)", [char_id])
@@ -83,19 +87,19 @@ class BanService:
"WHERE char_id = (SELECT main from account where char_id=?) and disabled=1",
[char_id])
- def get_ban_list(self):
+ def get_ban_list(self, full=True) -> list[DictObject]:
t = int(time.time())
- return self.db.query("SELECT b.*, COALESCE(p1.name, b.char_id) AS name, p2.name AS sender_name FROM ban_list b "
- "LEFT JOIN player p1 ON b.char_id = p1.char_id "
- "LEFT JOIN player p2 ON b.sender_char_id = p2.char_id "
- "WHERE ended_early != 1 AND (finished_at > ? OR finished_at = -1) "
- "ORDER BY b.created_at DESC", [t])
+ return self.db.query(f"SELECT b.*, COALESCE(p1.name, b.char_id) AS name, p2.name AS sender_name FROM ban_list b "
+ f"LEFT JOIN player p1 ON b.char_id = p1.char_id "
+ f"LEFT JOIN player p2 ON b.sender_char_id = p2.char_id "
+ f"WHERE ended_early < 1 AND (finished_at > ? {'OR finished_at = -1' if full else ''}) "
+ f"ORDER BY b.created_at DESC", [t])
def check_for_banned(self, context):
char_id = context.char_id
if self.get_ban(char_id):
# do nothing if character is banned
- self.logger.info("ignoring banned character %d for command '%s'" % (char_id, context.message))
+ self.logger.info(f"ignoring banned character {self.account_service.character_service.resolve_char_to_name(char_id, default=str(char_id))} for command '{context.message}'")
return False
else:
return True
diff --git a/modules/core/discord/discord_command_handler.py b/modules/core/discord/discord_command_handler.py
index e2b4ab4..e17d3ea 100644
--- a/modules/core/discord/discord_command_handler.py
+++ b/modules/core/discord/discord_command_handler.py
@@ -16,12 +16,14 @@ from core.db import DB
from core.decorators import instance, command, event, timerevent
from core.dict_object import DictObject
from core.logger import Logger
+from core.setting_types import BooleanSettingType
from modules.core.ban.ban_service import BanService
if TYPE_CHECKING:
from modules.core.discord.discord_controller import DiscordController
from core.message_hub_service import MessageHubService
from core.command_service import CommandService
+ from core.setting_service import SettingService
@instance()
@@ -37,9 +39,12 @@ class DiscordCommandHandler(BaseModule):
self.db: DB = registry.get_instance("db")
self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service")
self.command_service: CommandService = registry.get_instance("command_service")
+ self.setting_service: SettingService = registry.get_instance("setting_service")
def pre_start(self):
self.command_service.register_command_channel("Discord", self.DISCORD_CHANNEL)
+ self.setting_service.register(self.module_name, "include_discord_ban", True, BooleanSettingType(),
+ "Should chatbot bans be extended to discord (Ban from the discord server!):")
@event("discord_command", "should the bot take care of discord commands", False)
def discord_command_handler(self, _, event_data):
@@ -251,6 +256,8 @@ class DiscordCommandHandler(BaseModule):
token = self.discord.setting_discord_token().get_value()
if token.lower() in ["none", "", "null"]:
return
+ if not self.setting_service.get("include_discord_ban").get_value():
+ return
account = self.discord.account_service.get_account(event_data.char_id)
if account.discord_joined == 1 and account.discord_id != "":
member = self.discord.guild.get_member(int(account.discord_id))
diff --git a/modules/core/discord/discord_controller.py b/modules/core/discord/discord_controller.py
index e636d0d..6c70245 100644
--- a/modules/core/discord/discord_controller.py
+++ b/modules/core/discord/discord_controller.py
@@ -210,7 +210,7 @@ class DiscordController:
await invite.delete()
async def discord_banlist(self):
- return await self.guild.bans()
+ return [entry async for entry in self.guild.bans(limit=2000)]
async def discord_ban_user(self, user, reason=""):
await self.guild.ban(user, reason=reason)
@@ -239,6 +239,10 @@ class DiscordController:
addroles.append(failed)
remroles = discord_user.roles
else:
+ # if bans are not transferred to discord, just freeze the member role.
+ ignore = (not self.setting_service.get("include_discord_ban").get_value()) and account.disabled
+ if ignore:
+ self.logger.warning(f"Frozen Account: {account.name}")
# Superadmin
if access_level["level"] in [10, 20]:
if self.get_role(self.discord_roles.admin, discord_user.roles) is None:
@@ -255,7 +259,7 @@ class DiscordController:
rank = self.get_role(self.discord_roles.member, self.guild.roles) or None
if rank is not None:
addroles.append(rank)
- else:
+ elif not ignore:
rank = self.get_role(self.discord_roles.member, self.guild.roles) or None
if rank is not None:
remroles.append(rank)
diff --git a/modules/standard/loot/loot_controller.py b/modules/standard/loot/loot_controller.py
index 59c6d36..0f9b847 100644
--- a/modules/standard/loot/loot_controller.py
+++ b/modules/standard/loot/loot_controller.py
@@ -237,7 +237,7 @@ class LootController:
for i in remove:
self.loot_list.pop(i)
- msg = ChatBlob("Roll results", blob, "\n________________\n\nTime is UP!\n", "\n________________")
+ msg = ChatBlob("Roll results", blob, "\n________________\n\nTime is UP!\n", "\n________________")
self.bot.send_private_channel_message(msg if len(blob) > 0 else "No one was added to any loot")
if self.loot_list:
count = 1