From a3a26f2ba4103e9ad7b8717a9f7a9288d768a0f1 Mon Sep 17 00:00:00 2001 From: Minidodo Date: Fri, 15 Apr 2022 17:05:30 +0200 Subject: [PATCH] get rid of the MessageDistributor module... & update discord, to work with API v10 Added discord commands (issue: as they're running over the event hub, they're processed on the same track as other events. => activity ingame triggers the next run; otherwise there's some delay for responses) relay is a standard module now. --- bootstrap.py | 2 + core/chat_blob.py | 3 +- core/command_service.py | 42 +- core/conn.py | 4 +- core/igncore.py | 26 +- core/logger.py | 2 + core/message_hub_service.py | 4 +- core/setting_service.py | 1 + core/setting_types.py | 1 + modules/core/accounting/account_controller.py | 2 +- .../core/accounting/preference_controller.py | 7 +- modules/core/config/config.msg | 6 + modules/core/config/config_controller.py | 1 + .../core/discord/discord_command_handler.py | 382 ++++++++++++++ modules/core/discord/discord_controller.py | 474 ++++-------------- modules/core/discord/discord_data.py | 25 + modules/core/discord/discord_management.py | 82 +++ .../private_channel_controller.py | 11 +- modules/core/riadmin/riadmin_controller.py | 15 +- .../alliance/alliance_relay_controller.py | 224 ++++----- modules/onlinebot/online/org_controller.py | 7 +- modules/orgbot/org/org_controller.py | 10 +- modules/raidbot/raid/raidbot_controller.py | 3 - .../MessageDistributor/message_distributor.py | 274 +++++----- .../alliance/alliance_relay.py | 98 +++- modules/standard/helpbot/time_controller.py | 2 +- modules/standard/news/weekly_controller.py | 16 +- modules/standard/news/worldboss_controller.py | 15 +- modules/standard/online/online_display.py | 6 +- .../standard/specials/specials_controller.py | 12 +- .../whois/character_info_controller.py | 6 +- requirements.txt | 2 +- 32 files changed, 1036 insertions(+), 729 deletions(-) create mode 100644 modules/core/discord/discord_command_handler.py create mode 100644 modules/core/discord/discord_data.py create mode 100644 modules/core/discord/discord_management.py rename modules/{orgbot => standard}/alliance/alliance_relay.py (81%) diff --git a/bootstrap.py b/bootstrap.py index fceaed2..47b6b48 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -13,6 +13,8 @@ from core.logger import Logger from core.registry import Registry from core.upgrade import run_upgrades +import faulthandler +faulthandler.enable() def get_config_from_env(): config_obj = DictObject() diff --git a/core/chat_blob.py b/core/chat_blob.py index aeb5431..14384b3 100644 --- a/core/chat_blob.py +++ b/core/chat_blob.py @@ -1,9 +1,10 @@ class ChatBlob: - def __init__(self, title, msg, prefix="", suffix=""): + def __init__(self, title, msg, prefix="", suffix="", embed=True): self.title = title self.msg = msg.strip("\n") self.page_prefix = prefix self.page_postfix = suffix + self.embed = embed def __str__(self): return f"ChatBlob('{self.title}', '{self.msg}')" diff --git a/core/command_service.py b/core/command_service.py index bffd764..6e4ad5a 100644 --- a/core/command_service.py +++ b/core/command_service.py @@ -66,6 +66,10 @@ class CommandService: self.register_command_channel("Private Message", self.PRIVATE_MESSAGE_CHANNEL) self.register_command_channel("Org Channel", self.ORG_CHANNEL) self.register_command_channel("Private Channel", self.PRIVATE_CHANNEL) + self.relay_hub_service.register_message_source("tell_logger") + self.relay_hub_service.register_message_source("system_logger") + self.relay_hub_service.register_message_source("access_denied_logger") + self.relay_hub_service.register_message_source("member_logger") def start(self): access_levels = {} @@ -205,16 +209,10 @@ class CommandService: if not followup: if channel == "msg": if message.lower().startswith("mail"): - self.relay_hub_service.send_message("tell_logger", char_id, "mail |usage hidden|", - "mail |usage hidden|") + self.relay_hub_service.send_message("tell_logger", DictObject({'char_id': char_id, 'name': self.character_service.resolve_char_to_name(char_id)}), f"[FROM] {self.character_service.resolve_char_to_name(char_id)}: mail |usage hidden|", + f"[FROM] {self.character_service.resolve_char_to_name(char_id)}: mail |usage hidden|") else: - self.relay_hub_service.send_message("tell_logger", char_id, message, message) - else: - if message.lower().startswith("mail"): - self.relay_hub_service.send_message("dc_relay_log", char_id, "mail |usage hidden|", - "mail |usage hidden|") - else: - self.relay_hub_service.send_message("dc_relay_log", char_id, message, message) + self.relay_hub_service.send_message("tell_logger", DictObject({'char_id': char_id, 'name': self.character_service.resolve_char_to_name(char_id)}), f"[FROM] {self.character_service.resolve_char_to_name(char_id)}: {message}", f"[FROM] {self.character_service.resolve_char_to_name(char_id)}: {message}") # message = html.unescape(message) command_str, command_args = self.get_command_parts(message) @@ -253,12 +251,15 @@ class CommandService: self.relay_hub_service.send_message("access_denied_logger", sender, f"[ERROR] {sender.name}: {message}", f"[ERROR] {sender.name}: {message}") - self.bot.send_mass_message(char_id, self.getresp("global", "error_processing")) + if channel == "discord": + reply(self.getresp("global", "error_processing")) + else: + self.bot.send_mass_message(char_id, self.getresp("global", "error_processing")) # record command usage self.usage_service.add_usage(command_str, handler["callback"].__qualname__, char_id, channel) else: - self.access_denied_response(message, sender, cmd_config, reply) + self.access_denied_response(message, sender, cmd_config, reply, channel) else: # handlers were found, but no handler regex matched help_text = self.get_help_text(char_id, command_str, channel) @@ -266,7 +267,7 @@ class CommandService: reply(self.format_help_text(command_str, help_text)) else: # the command is known, but no help is returned, therefore user does not have access to command - self.access_denied_response(message, sender, cmd_config, reply) + self.access_denied_response(message, sender, cmd_config, reply, channel) # if access_level['label'] != "all": # self.relay_hub_service.send_message("access_denied_logger", sender, # f"[DENIED] {sender.name}: {message}", @@ -280,20 +281,29 @@ class CommandService: 0) self.relay_hub_service.send_message("access_denied_logger", sender, f"[ERROR] {sender.name}: {message}", f"[ERROR] {sender.name}: {message}") - self.bot.send_mass_message(char_id, self.getresp("global", "error_processing")) + if channel == "discord": + reply(self.getresp("global", "error_processing")) + else: + self.bot.send_mass_message(char_id, self.getresp("global", "error_processing")) def handle_unknown_command(self, command_str, command_args, channel, sender, reply): self.relay_hub_service.send_message("access_denied_logger", sender, f"[UNKNOWN] {sender.name}: {command_str} {command_args}", f"[UNKNOWN] {sender.name}: {command_str} {command_args}") if sender.access_level["label"] != "all": - self.bot.send_mass_message(sender.char_id, self.getresp("global", "unknown_command", {"cmd": command_str})) + if channel == "discord": + self.getresp("global", "unknown_command", {"cmd": command_str}) + else: + self.bot.send_mass_message(sender.char_id, self.getresp("global", "unknown_command", {"cmd": command_str})) - def access_denied_response(self, message, sender, cmd_config, reply): + def access_denied_response(self, message, sender, cmd_config, reply, channel): self.relay_hub_service.send_message("access_denied_logger", sender, f"[DENIED] {sender.name}: {message}", f"[DENIED] {sender.name}: {message}") if sender.access_level["label"] != "all": - self.bot.send_mass_message(sender.char_id, self.getresp("global", "access_denied")) + if channel == "discord": + reply(self.getresp("global", "access_denied")) + else: + self.bot.send_mass_message(sender.char_id, self.getresp("global", "access_denied")) def get_command_parts(self, message): parts = message.split(" ", 1) diff --git a/core/conn.py b/core/conn.py index b8844db..b35ddf4 100644 --- a/core/conn.py +++ b/core/conn.py @@ -53,8 +53,8 @@ class Conn(Bot): if num_messages > 30: self.logger.warning("automatically clearing outgoing message queue (%d messages)" % num_messages) self.packet_queue.clear() - elif num_messages > 10: - self.logger.warning("%d messages in outgoing message queue" % num_messages) + # elif num_messages > 10: + # self.logger.warning("%d messages in outgoing message queue" % num_messages) def __str__(self): return self.id diff --git a/core/igncore.py b/core/igncore.py index c98d5c8..7fd7d0c 100644 --- a/core/igncore.py +++ b/core/igncore.py @@ -1,6 +1,7 @@ import inspect import threading import time +import typing from conf.config import BotConfig from core.aochat import server_packets, client_packets @@ -11,15 +12,19 @@ from core.conn import Conn from core.db import DB from core.decorators import instance from core.dict_object import DictObject + from core.fifo_queue import FifoQueue from core.job_scheduler import JobScheduler from core.logger import Logger -from core.lookup.character_service import CharacterService -from core.public_channel_service import PublicChannelService -from core.setting_service import SettingService -from core.setting_types import BooleanSettingType from core.text import Text -from modules.core.accounting.services.access_service import AccessService +from core.setting_types import BooleanSettingType + +if typing.TYPE_CHECKING: + from core.lookup.character_service import CharacterService + from core.public_channel_service import PublicChannelService + from core.setting_service import SettingService + from modules.core.accounting.services.access_service import AccessService + from core.event_service import EventService @instance("bot") @@ -42,8 +47,8 @@ class IgnCore: self.dimension = None self.last_timer_event = 0 self.start_time = int(time.time()) - self.major_version = "IGNCore v2.8" - self.minor_version = "4" + self.major_version = "IGNCore v2.9" + self.minor_version = "0" self.incoming_queue = FifoQueue() self.mass_message_queue = None self.conns = DictObject() @@ -55,7 +60,7 @@ class IgnCore: self.text: Text = registry.get_instance("text") self.setting_service: SettingService = registry.get_instance("setting_service") self.access_service: AccessService = registry.get_instance("access_service") - self.event_service = registry.get_instance("event_service") + self.event_service: EventService = registry.get_instance("event_service") self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler") self.command_service = registry.get_instance("command_service") @@ -113,7 +118,7 @@ class IgnCore: "created_at INT NOT NULL, " "INDEX `command` (`command`) USING BTREE, " "INDEX `char_id` (`char_id`) USING BTREE, " - "INDEX `channel` (`channel`) USING BTREE) ENGINE MEMORY") + "INDEX `channel` (`channel`) USING BTREE)") # self.db.exec("UPDATE db_version SET verified = 0") self.db.exec("UPDATE db_version SET verified = 1 WHERE file = 'db_version'") @@ -440,5 +445,8 @@ class IgnCore: def get_char_name(self): return self.conns["main"].char_name + def get_conn(self): + return self.conns["main"] + def get_char_id(self): return self.conns["main"].char_id diff --git a/core/logger.py b/core/logger.py index e890e8d..c4558b1 100644 --- a/core/logger.py +++ b/core/logger.py @@ -32,6 +32,8 @@ class Logger: def format_chat_message(self, msg): msg = re.sub(r"", "[link]", msg, flags=re.UNICODE | re.DOTALL) msg = re.sub(r"", "[link]", msg, flags=re.UNICODE | re.DOTALL) + msg = re.sub(r"(\w+?)", "\1", msg, flags=re.UNICODE | re.DOTALL) + msg = re.sub(r"", "", msg, flags=re.UNICODE) msg = re.sub("", "", msg, flags=re.UNICODE) msg = re.sub("", "[/link]", msg, flags=re.UNICODE) diff --git a/core/message_hub_service.py b/core/message_hub_service.py index 65b0209..ee1c277 100644 --- a/core/message_hub_service.py +++ b/core/message_hub_service.py @@ -50,7 +50,9 @@ class MessageHubService: f"Incorrect number of arguments for handler '{callback.__module__}.{callback.__name__}()'") if destination in self.hub: - raise Exception(f"Message hub destination '{destination}' already subscribed") + self.logger.error(f"Message hub destination '{destination}' already subscribed") + return + # raise Exception(f"Message hub destination '{destination}' already subscribed") for source in default_sources: if source not in self.sources: diff --git a/core/setting_service.py b/core/setting_service.py index 8b3dab2..5de59d8 100644 --- a/core/setting_service.py +++ b/core/setting_service.py @@ -39,6 +39,7 @@ class SettingService: if module.split(".")[0] not in self.bot.modules: return setting.set_name(name) + setting.module = module setting.set_description(description) setting.set_extended_description(extended_description) diff --git a/core/setting_types.py b/core/setting_types.py index 19484a2..119df29 100644 --- a/core/setting_types.py +++ b/core/setting_types.py @@ -9,6 +9,7 @@ class SettingType: def __init__(self): self.setting_service = Registry.get_instance("setting_service") self.name = None + self.module = "" def set_name(self, name): self.name = name diff --git a/modules/core/accounting/account_controller.py b/modules/core/accounting/account_controller.py index e1d34c0..49201e4 100644 --- a/modules/core/accounting/account_controller.py +++ b/modules/core/accounting/account_controller.py @@ -274,4 +274,4 @@ class AccountController: rows = self.account_service.get_logs(alts[0].char_id, limit=20) for i in rows: response += self.account_service.format_entry(i) - return self.text.format_page(f"{alts[0].name}'s Account", response) + return ChatBlob(f"{alts[0].name}'s Account", response) diff --git a/modules/core/accounting/preference_controller.py b/modules/core/accounting/preference_controller.py index 454cc41..0d2077d 100644 --- a/modules/core/accounting/preference_controller.py +++ b/modules/core/accounting/preference_controller.py @@ -1,3 +1,5 @@ +import typing + from core.buddy_service import BuddyService from core.chat_blob import ChatBlob from core.command_alias_service import CommandAliasService @@ -11,8 +13,9 @@ from core.text import Text from core.translation_service import TranslationService from core.igncore import IgnCore from core.util import Util -from modules.core.accounting.services.account_service import AccountService -from modules.core.discord.discord_controller import DiscordController +if typing.TYPE_CHECKING: + from modules.core.accounting.services.account_service import AccountService + from modules.core.discord.discord_controller import DiscordController @instance() diff --git a/modules/core/config/config.msg b/modules/core/config/config.msg index 52751fe..ebd0b72 100644 --- a/modules/core/config/config.msg +++ b/modules/core/config/config.msg @@ -135,6 +135,12 @@ "en_US": "Could not find setting {setting}.", "de_DE": "Die Einstellung {setting} wurde nicht gefunden." }, + + "setting_owner": { + "en_US": "Setting owned by module: {value}\n", + "de_DE": "Diese Einstellung gehört zu dem Modul: {value}\n" + + }, "current_value": { "en_US": "Current Value: {value}\n", "de_DE": "Aktueller Wert: {value}\n" diff --git a/modules/core/config/config_controller.py b/modules/core/config/config_controller.py index a5ae0f1..3a1a1c8 100644 --- a/modules/core/config/config_controller.py +++ b/modules/core/config/config_controller.py @@ -160,6 +160,7 @@ class ConfigController: setting = self.setting_service.get(setting_name) if setting: + blob += self.getresp("module/config", "setting_owner", {"value": str(setting.module)}) blob += self.getresp("module/config", "current_value", {"value": str(setting.get_display_value())}) blob += self.getresp("module/config", "description", {"desc": setting.get_description()}) if setting.get_extended_description(): diff --git a/modules/core/discord/discord_command_handler.py b/modules/core/discord/discord_command_handler.py new file mode 100644 index 0000000..12944a1 --- /dev/null +++ b/modules/core/discord/discord_command_handler.py @@ -0,0 +1,382 @@ +import asyncio +import threading +import time + +import re +from mailbox import Message +from typing import TYPE_CHECKING + +from discord import Activity, ActivityType, Message, Embed + +from core.aochat.BaseModule import BaseModule +from core.chat_blob import ChatBlob +from core.command_param_types import Const + +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 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 + + +@instance() +class DiscordCommandHandler(BaseModule): + DISCORD_CHANNEL = "discord" + + def __init__(self): + self.logger = Logger(__name__) + + def inject(self, registry): + self.bot = registry.get_instance("bot") + self.discord: DiscordController = registry.get_instance("discord_controller") + 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") + + def pre_start(self): + self.command_service.register_command_channel("Discord", self.DISCORD_CHANNEL) + + @event("discord_command", "should the bot take care of discord commands", False) + def discord_command_handler(self, _, event_data): + acc = event_data.account + ctx: Message = event_data.message + message = ctx.clean_content + if message.startswith(self.discord.setting_service.get("symbol").get_value()): + threading.Thread(target=self.command_service.process_command( + self.command_service.trim_command_symbol(message), + self.DISCORD_CHANNEL, + acc.main, + lambda msg: self.send_response(ctx, msg), + self.discord.bot.get_conn(), + False), daemon=True).start() + + def send_response(self, ctx: Message, reply): + rsp = "" + embeds = [] + if type(reply) == str: + reply = self.discord.text.format_message(reply) + rsp = f"> {self.parseDiscord(reply)}" + self.discord.relay_hub_service.send_message(f"Discord_({ctx.channel.name})", + DictObject({'char_id': 0, 'name': ctx.author.name, + 'discord_handle': + f'{ctx.author.name}#{ctx.author.discriminator}'}), + reply, reply) + if type(reply) == ChatBlob: + if reply.embed: + embeds.append(Embed(title=self.parseDiscord(f"{reply.page_prefix} {reply.title} {reply.page_postfix}"), + color=0x00FF00, + description=self.parseDiscord(reply.msg).replace("\n> ", '\n'))) + + else: + reply: ChatBlob + rsp = self.parseDiscord(reply.page_prefix) + "\n" + if not (self.discord.text.strip_html_tags(reply.msg) or reply.msg).startswith(reply.title): + rsp += f"**__{reply.title}__**\n" + rsp += "> " + self.parseDiscord(reply.msg) + rsp += f"\n {self.parseDiscord(reply.page_postfix)}" + self.discord.relay_hub_service.send_message(f"Discord_({ctx.channel.name})", + DictObject({'char_id': 0, 'name': ctx.author.name, + 'discord_handle': + f'{ctx.author.name}#{ctx.author.discriminator}'}), + reply, reply) + if len(rsp) > 2000: + rsp = self.discord.text.split_by_separators(rsp, 2000) + for page in rsp: + self.discord.client.loop.create_task(ctx.reply(page)) + else: + self.discord.client.loop.create_task(ctx.reply(rsp, embeds=embeds)) + + def parseDiscord(self, ctx: str): + proficon = {1: "sold", 2: "ma", 3: "eng", 4: "fixer", 5: "agent", 6: "adv", 7: "trad", 8: "crat", 9: "enf", + 10: "doc", 11: "nt", 12: "mp", 14: "keep", 15: "shade"} + m = re.findall(r"", ctx) + for match in m: + ctx = ctx.replace(f"", + f"#_{self.discord.util.get_profession(proficon.get(int(match)))}#_") + + for x in ["`", ' *', ' _', ' |']: + ctx = ctx.replace(x, f' \\{x.strip()}') + for pattern, sub in [(r"(
|\n|
)", r"\n> "), + (r"(.*?)", r"**\1**"), + (r"(.*?)", r"**__\1__**"), + (r"
(.*?)
", r"**__\1__**"), + (r"(.*?)", r"__\1__"), + (r"(.*?)", r"*\1*"), + (r"(.*?)", r'\2'), + (r"", self.bot.get_char_name()), + (r"", self.discord.setting_service.get_value("symbol")), + (r"(.+?)<\/a>", r"`\2`"), + (r"<(.+?)>\s*?<\1>", ''), + (r"", ''), + (r"(.*?)", r'`\1`'), + (r"(.*?)", r":yellow_circle: \1"), + (r"(.*?)", r":blue_circle: \1"), + (r"(.*?)", r":white_circle: \1"), + (r"", "\t"), + (r"", ''), + ('#', ''), + ('<', '<'), + ('>', '>')]: + ctx = re.sub(pattern, sub, ctx) + + cnt = 1 + while cnt > 0: + ctx, cnt = re.subn(r"<(black|white|yellow|blue|green|red|orange|grey|cyan|violet)>(.*?|\d+?)", r"\2", + ctx) + + ctx.rstrip() + ctx.rstrip(">") + return ctx + + @command(command="discord", params=[Const("invite")], access_level="member", + description="Get a personal Discord invite", sub_command="invite") + def discord_invite_cmd(self, request, _): + if not self.discord.client: + return "Discord module has not been initiated yet. Please try again later." + account = self.discord.account_service.get_account(request.sender.char_id) + if account is None: + return "You do not have an account" + if account.disabled == 1: + return "Your account is disabled" + if account.discord_joined == 1: + return "You have already joined the Discord server" + if account.discord_invite != "": + for ginvite in self.discord.invites: + if ginvite.code == account.discord_invite: + asyncio.run_coroutine_threadsafe(self.discord.discord_delete_invite(ginvite), self.discord.loop) + invite = asyncio.run_coroutine_threadsafe(self.discord.discord_create_invite(account.name), self.discord.loop) + invite = invite.result() + self.discord.data.set_discord_invite(account.char_id, invite.code) + self.bot.send_mass_message(request.sender.char_id, + f"Your personal Discord invite is: {invite} \n" + f"This invite is only valid for 5 minutes.") + + @command(command="discord", params=[Const("update")], access_level="member", + description="Update your discord information", sub_command="update") + def discord_update_cmd(self, request, _): + if not self.discord.client: + return "Discord module has not been initiated yet. Please try again later." + return self.discord.discord_update_account(request.sender.char_id) + + @command(command="discord", params=[Const("disconnect")], access_level="admin", + description="Disconnect from Discord", sub_command="admin") + def discord_disconnect_cmd(self, _, _1): + if not self.discord.client: + return "Discord module has not been initiated yet. Please try again later." + if self.discord.client.is_closed(): + return "Discord is already disconnected" + else: + asyncio.run_coroutine_threadsafe(self.discord.discord_disconnect(), self.discord.loop) + return "Disconnecting Discord" + + @command(command="discord", + params=[Const("members")], + access_level="admin", + description="Get all discord members", + sub_command="members") + def discord_members_cmd(self, _, _1): + if not self.discord.client: + return "Discord module has not been initiated yet. Please try again later." + blob = "" + for member in self.discord.guild.members: + member_roles = [] + for role in member.roles: + if role.name == "@everyone": # Skip @everyone as everyone has it. + continue + member_roles.append(f"{role.name}") + member_roles.sort(key=str.lower) + blob += f"{member.name + '#' + member.discriminator} ({', '.join(member_roles)})\n" + return ChatBlob(f"Discord Members ({len(self.discord.guild.members):d})", blob) + + @event(event_type="connect", description="Connects the Discord client automatically on startup, if a token exists") + def handle_connect_event(self, _, _1): + token = self.discord.setting_discord_token().get_value() + if token.lower() in ["none", "", "null"]: + return + + # noinspection PyTypeChecker + self.discord.loop = asyncio.get_event_loop() + self.discord.loop.create_task(self.discord.discord_connect(token)) + self.discord.thread = threading.Thread(target=self.discord.run_it_forever, daemon=True) + self.discord.thread.start() + + @event(event_type=BanService.BAN_ADDED_EVENT, description="Ban user from Discord") + def ban_added_event(self, _, event_data): + token = self.discord.setting_discord_token().get_value() + if token.lower() in ["none", "", "null"]: + 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)) + if member is not None: + ban = asyncio.run_coroutine_threadsafe(self.discord.discord_ban_user(member, event_data.reason), + self.discord.client.loop) + + @event(event_type=BanService.BAN_REMOVED_EVENT, description="Remove Discord ban") + def ban_removed_event(self, _, event_data): + token = self.discord.setting_discord_token().get_value() + if token.lower() in ["none", "", "null"]: + return + account = self.discord.account_service.get_account(event_data.char_id) + if account.discord_id != "": + banlist = asyncio.run_coroutine_threadsafe(self.discord.discord_banlist(), self.discord.client.loop) + banlist = banlist.result() + for ban in banlist: + if ban.user.id == int(account.discord_id): + unban = asyncio.run_coroutine_threadsafe(self.discord.discord_unban_user(ban.user), + self.discord.client.loop) + + @timerevent(budatime="1s", description="Handle Discord queue") + def timer_check_discord_queue(self, _, _1): + while self.discord.discord_queue: + t = int(time.time()) + obj = self.discord.discord_queue.pop(0) + if obj.type == "on_member_remove": + member = obj.member + handle = member.name + "#" + member.discriminator + + account = self.discord.data.get_account_discord_id(member.id) + if not account: + log = '**%s** has left discord (**%s**)' % (member.nick or member.name, handle) + self.relay_hub_service.send_message("system_logger", 0, log, log) + self.logger.info(log) + continue + main = self.discord.account_service.get_main(account.char_id) + if account is None: + continue + log = '**%s** has left discord (**%s**)' % (main.name, handle) + self.relay_hub_service.send_message("system_logger", 0, log, log) + self.logger.info(log) + # self.bot.send_private_channel_message("%s has left Discord (%s)" % (main.name, handle)) + self.discord.data.set_discord_left(account.main) + if account.discord_handle != handle: + self.discord.data.set_discord_handle(member.id, handle) + if obj.type == "on_member_join": + member = obj.member + invite_used = obj.invite + handle = member.name + "#" + member.discriminator + if invite_used is None: + log = '**%s** joined discord with unknown invite' % handle + self.relay_hub_service. \ + send_message("system_logger", 0, + log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " + f"check that!", + log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " + f"check that!") + self.logger.info(log) + continue + self.discord.guild = self.discord.client.get_guild(self.discord.client.guilds[0].id) + account = self.discord.data.get_discord_invite(invite_used.code) + if account is None: + log = '**%s** joined discord with invite **%s** but couldnt find account!' % ( + handle, invite_used.code) + self.relay_hub_service. \ + send_message("system_logger", 0, + log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " + f"check that!", + log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " + f"check that!") + self.logger.info(log) + asyncio.run_coroutine_threadsafe(self.discord.discord_member_roles(member, None, None), + self.discord.loop) + continue + + main = self.discord.account_service.get_main(account.main) + self.discord.data.set_discord_joined(account.main, handle, member.id) + if self.discord.setting_service.get_value('is_alliance_bot') == '1': + nick = f"{f'[{self.discord.alias_controller.get_alias(main.org_id)}] '}{main.name}" + else: + nick = f"{main.name}" + nick = asyncio.run_coroutine_threadsafe(self.discord.discord_member_nick(member, nick), + self.discord.loop) + access_level = access_level = self.discord.access_service.get_access_level(account.main) + roles = asyncio.run_coroutine_threadsafe( + self.discord.discord_member_roles(member, account, access_level), + self.discord.loop) + # noinspection LongLine + log = '**%s** joined discord with invite **%s** and matches account **%s**' \ + % (handle, invite_used.code, + f"{f'[{self.discord.alias_controller.get_alias(main.org_id)}] ' if self.discord.setting_service.get_value('is_alliance_bot') == '1' else ''}{main.name}") + self.relay_hub_service.send_message("system_logger", account.main, log, log) + self.logger.info(log) + + @timerevent(budatime="1h", description="Verify Discord members", run_at_startup=True) + def timer_check_discord_members(self, event_type, event_data): + token = self.discord.setting_discord_token().get_value() + if token.lower() in ["none", "", "null"]: + return + if not self.bot.is_ready(): + return + update = asyncio.run_coroutine_threadsafe(self.discord.discord_update_bot_basic(), self.discord.loop) + # Update accounts that have left/joined discord without the bot being online + accounts = self.db.query("SELECT * FROM account WHERE discord_id !=''") + for account in accounts: + match = False + for member in self.discord.guild.members: + handle = member.name + "#" + member.discriminator + if member.id == int(account.discord_id): + match = True + if account.discord_joined == 0: + self.discord.data.set_discord_joined(account.main, handle, member.id) + break + if match is False: + if account.discord_joined == 1: + self.discord.data.set_discord_left(account.main) + # Update current discord Members + accounts = self.discord.db.query("SELECT * FROM account WHERE discord_joined = 1 and char_id = main") + for member in self.discord.guild.members: + if member.id == self.discord.client.user.id: + continue + member_account = None + + for account in list(accounts): + if int(account.discord_id) == member.id: + member_account = account + accounts.remove(account) + break + access_level = 0 + if member_account is not None: + access_level = self.discord.access_service.get_access_level(member_account.main) + roles = asyncio.run_coroutine_threadsafe( + self.discord.discord_member_roles(member, member_account, access_level), + self.discord.loop) + if member_account is not None: + main = self.discord.pork.get_character_info(member_account.main) + # noinspection LongLine + nick = f"{f'[{self.discord.alias_controller.get_alias(main.org_id)}] ' if self.discord.setting_service.get_value('is_alliance_bot') == '1' else ''}{main.name}" + + nick = asyncio.run_coroutine_threadsafe(self.discord.discord_member_nick(member, nick), + self.discord.loop) + + @event(event_type="main_changed", description="Fix discord names & ranks") + def fix_ranks(self, _, data): + row = self.db.query_single("SELECT * from account where char_id=?", [data.old_main_id]) + if row.discord_joined == 0: + self.logger.debug(f"{data.old_main_id} was not in discord, ignoring main fixing") + return + elif row.discord_joined == 1: + self.db.exec( + "UPDATE account set discord_handle=?, discord_id=?, discord_invite=?, discord_joined=? where char_id=?", + [row.discord_handle, row.discord_id, row.discord_invite, row.discord_joined, data.new_main_id]) + self.db.exec( + "UPDATE account set discord_handle='', discord_id=0, discord_invite='', discord_joined=0 " + "where char_id=?", + [data.old_main_id]) + self.logger.info(f"{data.old_main_id} was in discord, overwriting {data.new_main_id} with {data}") + + self.discord.discord_update_account(data.new_main_id) + + @timerevent(budatime="5m", description="update activity") + def change_count(self, _, _1): + if hasattr(self, "loop"): + count = self.db.query_single('SELECT count(*) as count from online ' + 'where char_id NOT IN (select char_id from org_bots) and bot=?', + [self.bot.get_char_id()]).count + act = Activity(type=ActivityType.listening, + name=f"{count} Players") + asyncio.run_coroutine_threadsafe(self.discord.client.change_presence(activity=act), self.discord.loop) diff --git a/modules/core/discord/discord_controller.py b/modules/core/discord/discord_controller.py index e5c7b6c..25e8345 100644 --- a/modules/core/discord/discord_controller.py +++ b/modules/core/discord/discord_controller.py @@ -1,36 +1,38 @@ import asyncio -import html import logging import re -import threading import time -from asyncio import BaseEventLoop +from datetime import datetime, timedelta +from functools import partial from html.parser import HTMLParser # noinspection PyPackageRequirements -import discord -import emojis as emojis -# noinspection PyPackageRequirements -from discord import Message, TextChannel, Guild, Embed, Role +from typing import TYPE_CHECKING +# noinspection PyPackageRequirements +import discord +from discord import Message, TextChannel, Embed, Role, Guild from core.chat_blob import ChatBlob -from core.command_param_types import Const from core.db import DB -from core.decorators import instance, command, event, timerevent +from core.decorators import instance, event, timerevent from core.dict_object import DictObject from core.logger import Logger -from core.lookup.character_service import CharacterService -from core.lookup.pork_service import PorkService -from core.message_hub_service import MessageHubService -from core.setting_service import SettingService -from core.setting_types import HiddenSettingType +from core.setting_types import HiddenSettingType, NumberSettingType, TextSettingType, BooleanSettingType from core.text import Text from core.igncore import IgnCore from core.util import Util -from modules.core.accounting.services.access_service import AccessService -from modules.core.accounting.services.account_service import AccountService -from modules.core.ban.ban_service import BanService -from modules.onlinebot.online.org_alias_controller import OrgAliasController +from modules.core.discord.discord_data import DiscordData +from modules.core.discord.discord_management import DiscordManager + +if TYPE_CHECKING: + from modules.core.accounting.services.access_service import AccessService + from modules.core.accounting.services.account_service import AccountService + from modules.onlinebot.online.org_alias_controller import OrgAliasController + from modules.core.discord.discord_command_handler import DiscordCommandHandler + from core.lookup.character_service import CharacterService + from core.lookup.pork_service import PorkService + from core.message_hub_service import MessageHubService + from core.setting_service import SettingService class MLStripper(HTMLParser): @@ -61,18 +63,11 @@ class DiscordController: logging.getLogger("discord.gateway").setLevel(logging.WARN) logging.getLogger("discord.client").setLevel(logging.WARN) intents = discord.Intents.all() + intents.message_content = True self.client = discord.Client(intents=intents, chunk_guilds_at_startup=True) - self.client.event(self.on_ready) - self.client.event(self.on_member_join) - self.client.event(self.on_member_remove) - self.client.event(self.on_member_update) - self.client.event(self.on_guild_role_delete) - self.client.event(self.on_guild_role_create) - self.client.event(self.on_guild_role_update) - self.client.event(self.on_invite_create) - self.client.event(self.on_invite_delete) + self.manager: DiscordManager = DiscordManager(self) self.client.event(self.on_message) - self.guild = None + self.guild: Guild = None self.channel = None self.thread = None self.invites = None @@ -106,9 +101,17 @@ class DiscordController: self.account_service: AccountService = registry.get_instance("account_service") self.setting_service: SettingService = registry.get_instance("setting_service") + self.data: DiscordData = DiscordData(self.db, self.client) + self.cmd: DiscordCommandHandler = registry.get_instance("discord_command_handler") + def pre_start(self): self.setting_service.register(self.module_name, "discord_token", "", HiddenSettingType(allow_empty=True), "Enter your Discord token here") + self.setting_service.register(self.module_name, "public_relay", "[0]", TextSettingType(), + "Public discord relay (purpose: autodelete messages after 1h)") + self.setting_service.register(self.module_name, "channel_blacklist", "[0]", TextSettingType(), + "Channels which should not trigger the command handler") + self.bot.event_service.register_event_type("discord_command") def get_name(self, discord_id): data = self.db.query_single( @@ -118,242 +121,9 @@ class DiscordController: def setting_discord_token(self): return self.setting_service.get("discord_token") - @command(command="discord", params=[Const("invite")], access_level="member", - description="Get a personal Discord invite", sub_command="invite") - def discord_invite_cmd(self, request, _): - if not self.client: - return "Discord module has not been initiated yet. Please try again later." - account = self.account_service.get_account(request.sender.char_id) - if account is None: - return "You do not have an account" - if account.disabled == 1: - return "Your account is disabled" - if account.discord_joined == 1: - return "You have already joined the Discord server" - if account.discord_invite != "": - for ginvite in self.invites: - if ginvite.code == account.discord_invite: - asyncio.run_coroutine_threadsafe(self.discord_delete_invite(ginvite), self.loop) - invite = asyncio.run_coroutine_threadsafe(self.discord_create_invite(account.name), self.loop) - invite = invite.result() - self.set_discord_invite(account.char_id, invite.code) - self.bot.send_mass_message(request.sender.char_id, - f"Your personal Discord invite is: {invite} \n" - f"This invite is only valid for 5 minutes.") - - @command(command="discord", params=[Const("update")], access_level="member", - description="Update your discord information", sub_command="update") - def discord_update_cmd(self, request, _): - if not self.client: - return "Discord module has not been initiated yet. Please try again later." - return self.discord_update_account(request.sender.char_id) - - @command(command="discord", params=[Const("disconnect")], access_level="admin", - description="Disconnect from Discord", sub_command="admin") - def discord_disconnect_cmd(self, _, _1): - if not self.client: - return "Discord module has not been initiated yet. Please try again later." - if self.client.is_closed(): - return "Discord is already disconnected" - else: - asyncio.run_coroutine_threadsafe(self.discord_disconnect(), self.loop) - return "Disconnecting Discord" - - @command(command="discord", - params=[Const("members")], - access_level="admin", - description="Get all discord members", - sub_command="members") - def discord_members_cmd(self, _, _1): - if not self.client: - return "Discord module has not been initiated yet. Please try again later." - blob = "" - for member in self.guild.members: - member_roles = [] - for role in member.roles: - if role.name == "@everyone": # Skip @everyone as everyone has it. - continue - member_roles.append("%s" % role.name) - member_roles.sort(key=str.lower) - blob += "%s (%s)\n" % (member.name + "#" + member.discriminator, ", ".join(member_roles)) - return ChatBlob("Discord Members (%d)" % (len(self.guild.members)), blob) - - @event(event_type="connect", description="Connects the Discord client automatically on startup, if a token exists") - def handle_connect_event(self, _, _1): - token = self.setting_discord_token().get_value() - if token.lower() in ["none", "", "null"]: - return - - # noinspection PyTypeChecker - self.loop: BaseEventLoop = asyncio.get_event_loop() - self.loop.create_task(self.discord_connect(token)) - self.thread = threading.Thread(target=self.run_it_forever, daemon=True) - self.thread.start() - - @event(event_type=BanService.BAN_ADDED_EVENT, description="Ban user from Discord") - def ban_added_event(self, _, event_data): - token = self.setting_discord_token().get_value() - if token.lower() in ["none", "", "null"]: - return - account = self.account_service.get_account(event_data.char_id) - if account.discord_joined == 1 and account.discord_id != "": - member = self.guild.get_member(int(account.discord_id)) - if member is not None: - ban = asyncio.run_coroutine_threadsafe(self.discord_ban_user(member, event_data.reason), self.loop) - - @event(event_type=BanService.BAN_REMOVED_EVENT, description="Remove Discord ban") - def ban_removed_event(self, _, event_data): - token = self.setting_discord_token().get_value() - if token.lower() in ["none", "", "null"]: - return - account = self.account_service.get_account(event_data.char_id) - if account.discord_id != "": - banlist = asyncio.run_coroutine_threadsafe(self.discord_banlist(), self.loop) - banlist = banlist.result() - for ban in banlist: - if ban.user.id == int(account.discord_id): - unban = asyncio.run_coroutine_threadsafe(self.discord_unban_user(ban.user), self.loop) - - @timerevent(budatime="1s", description="Handle Discord queue") - def timer_check_discord_queue(self, _, _1): - while self.discord_queue: - t = int(time.time()) - obj = self.discord_queue.pop(0) - if obj.type == "on_member_remove": - member = obj.member - handle = member.name + "#" + member.discriminator - - account = self.get_account_discord_id(member.id) - if not account: - log = '**%s** has left discord (**%s**)' % (member.nick or member.name, handle) - self.relay_hub_service.send_message("system_logger", 0, log, log) - self.logger.info(log) - continue - main = self.account_service.get_main(account.char_id) - if account is None: - continue - log = '**%s** has left discord (**%s**)' % (main.name, handle) - self.relay_hub_service.send_message("system_logger", 0, log, log) - self.logger.info(log) - # self.bot.send_private_channel_message("%s has left Discord (%s)" % (main.name, handle)) - self.set_discord_left(account.main) - if account.discord_handle != handle: - self.set_discord_handle(member.id, handle) - if obj.type == "on_member_join": - member = obj.member - invite_used = obj.invite - handle = member.name + "#" + member.discriminator - if invite_used is None: - log = '**%s** joined discord with unknown invite' % handle - self.relay_hub_service. \ - send_message("system_logger", 0, - log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " - f"check that!", - log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " - f"check that!") - self.logger.info(log) - continue - self.guild: Guild = self.client.get_guild(self.client.guilds[0].id) - account = self.get_discord_invite(invite_used.code) - if account is None: - log = '**%s** joined discord with invite **%s** but couldnt find account!' % ( - handle, invite_used.code) - self.relay_hub_service. \ - send_message("system_logger", 0, - log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " - f"check that!", - log + f" {self.get_role('Administrator', self.guild.roles).mention}'s, " - f"check that!") - self.logger.info(log) - asyncio.run_coroutine_threadsafe(self.discord_member_roles(member, None, None), self.loop) - continue - - main = self.account_service.get_main(account.main) - self.set_discord_joined(account.main, handle, member.id) - if self.setting_service.get_value('is_alliance_bot') == '1': - nick = f"{f'[{self.alias_controller.get_alias(main.org_id)}] '}{main.name}" - else: - nick = f"{main.name}" - nick = asyncio.run_coroutine_threadsafe(self.discord_member_nick(member, nick), self.loop) - access_level = access_level = self.access_service.get_access_level(account.main) - roles = asyncio.run_coroutine_threadsafe(self.discord_member_roles(member, account, access_level), - self.loop) - # noinspection LongLine - log = '**%s** joined discord with invite **%s** and matches account **%s**' \ - % (handle, invite_used.code, - f"{f'[{self.alias_controller.get_alias(main.org_id)}] ' if self.setting_service.get_value('is_alliance_bot') == '1' else ''}{main.name}") - self.relay_hub_service.send_message("system_logger", account.main, log, log) - self.logger.info(log) - - @timerevent(budatime="1h", description="Verify Discord members", run_at_startup=True) - def timer_check_discord_members(self, event_type, event_data): - token = self.setting_discord_token().get_value() - if token.lower() in ["none", "", "null"]: - return - if not self.bot.is_ready(): - return - update = asyncio.run_coroutine_threadsafe(self.discord_update_bot_basic(), self.loop) - # Update accounts that have left/joined discord without the bot being online - accounts = self.db.query("SELECT * FROM account WHERE discord_id !=''") - for account in accounts: - match = False - for member in self.guild.members: - handle = member.name + "#" + member.discriminator - if member.id == int(account.discord_id): - match = True - if account.discord_joined == 0: - self.set_discord_joined(account.main, handle, member.id) - break - if match is False: - if account.discord_joined == 1: - self.set_discord_left(account.main) - # Update current discord Members - accounts = self.db.query("SELECT * FROM account WHERE discord_joined = 1 and char_id = main") - for member in self.guild.members: - if member.id == self.client.user.id: - continue - member_account = None - - for account in list(accounts): - if int(account.discord_id) == member.id: - member_account = account - accounts.remove(account) - break - access_level = 0 - if member_account is not None: - access_level = self.access_service.get_access_level(member_account.main) - roles = asyncio.run_coroutine_threadsafe(self.discord_member_roles(member, member_account, access_level), - self.loop) - if member_account is not None: - main = self.pork.get_character_info(member_account.main) - # noinspection LongLine - nick = f"{f'[{self.alias_controller.get_alias(main.org_id)}] ' if self.setting_service.get_value('is_alliance_bot') == '1' else ''}{main.name}" - - nick = asyncio.run_coroutine_threadsafe(self.discord_member_nick(member, nick), self.loop) - def run_it_forever(self): self.loop.run_forever() - def set_discord_invite(self, char_id, invite): - return self.db.exec("UPDATE account SET discord_invite = ? WHERE main = ?", [invite, char_id]) - - def set_discord_joined(self, char_id, handle, discord_id): - return self.db.exec("UPDATE account SET discord_joined = 1, discord_handle = ?, discord_id = ? WHERE main = ?", - [handle, discord_id, char_id]) - - def set_discord_left(self, char_id): - return self.db.exec("UPDATE account SET discord_joined = 0 WHERE main = ?", [char_id]) - - def set_discord_handle(self, discord_id, handle): - return self.db.exec("UPDATE account SET discord_handle = ? WHERE discord_id = ?", [handle, discord_id]) - - def get_discord_invite(self, invite): - return self.db.query_single("SELECT * FROM account WHERE discord_invite = ?", [invite]) - - def get_account_discord_id(self, discord_id): - return self.db.query_single( - "SELECT * FROM account a left join player p on a.char_id=p.char_id WHERE discord_id = ?", [discord_id]) - def discord_update_account(self, char_id): account = self.account_service.get_account(char_id) if account is None: @@ -371,65 +141,12 @@ class DiscordController: return "Processing..." return "No Discord account found" - def discord_invite_used(self, temp_invites): - for temp_invite in temp_invites: - # if temp_invite.inviter.id == self.client.user.id: - # ## Using this line limits it to invite created by the account the Discord bot runs as.. - for invite in self.invites: - if int(temp_invite.uses) > int(invite.uses) and temp_invite.code == invite.code: - return temp_invite - return None - def get_role(self, name, roles) -> Role or None: for role in roles: if role.name == name: return role return None - async def on_member_join(self, member): - temp_invites = await self.guild.invites() - invite_used = self.discord_invite_used(temp_invites) - self.discord_queue.append(DictObject({"type": "on_member_join", "member": member, "invite": invite_used})) - await invite_used.delete() - - async def on_member_remove(self, member): - self.discord_queue.append(DictObject({"type": "on_member_remove", "member": member})) - await self.discord_update_bot_full() - - async def on_ready(self): - await self.discord_update_bot_full() - count = self.db.query_single('SELECT count(*) as count from online ' - 'where char_id NOT IN (select char_id from org_bots) and bot=?', - [self.bot.get_char_id()]).count - - act = discord.Activity(type=discord.ActivityType.listening, - name=f"{count} Players") - asyncio.run_coroutine_threadsafe(self.client.change_presence(activity=act), self.loop) - await self.clean_channel() - - if self.guild.large: - self.logger.error( - f"Guild {self.guild.name} is classified as large, " - f"you need to request offline members to manage roles properly, this is not yet implemented") - - async def on_member_update(self, member_before, member_after): - await self.discord_update_bot_basic() - - async def on_guild_role_create(self, role): - await self.discord_update_bot_full() - - async def on_guild_role_delete(self, role): - await self.discord_update_bot_full() - - async def on_guild_role_update(self, role_before, role_after): - await self.discord_update_bot_full() - - async def on_invite_create(self, invite): - await self.discord_update_bot_full() - - async def on_invite_delete(self, invite): - await self.discord_update_bot_full() - async def discord_update_bot_basic(self): self.guild = self.client.get_guild(self.client.guilds[0].id) self.channel = self.client.get_channel(self.guild.text_channels[0].id) @@ -448,7 +165,37 @@ class DiscordController: await self.client.start(token) except discord.ClientException as exc: - self.logger.error("Something broke, I'm out!: %s" % str(exc)) + self.logger.error(f"Something broke, I'm out!: {str(exc)}") + + async def setup_relayhub(self): + for channel in self.guild.channels: + if type(channel) == TextChannel: + channel: TextChannel + self.relay_hub_service.register_message_source(f"Discord_({channel.name.replace(' ', '_')})") + self.relay_hub_service.register_message_destination(f"Discord_({channel.name.replace(' ', '_')})", + partial(self.relay, channel.id), [], [f"Discord_({channel.name.replace(' ', '_')})"]) + + def relay(self, channel, ctx): + ch: TextChannel = self.guild.get_channel(channel) + msg = ctx.formatted_message + del_after = 3600 if f"{ch.id}" in self.setting_service.get_value("public_relay").lstrip("[").rstrip("]").split(',') else None + if ctx.source == "alliance": + msg = self.cmd.parseDiscord(ctx.message) + elif type(msg) == ChatBlob: + if msg.embed: + msg: ChatBlob + blob = Embed(title=msg.title, color=0x00FF00, description=self.cmd.parseDiscord(msg.msg).replace("\n> ", '\n')) + asyncio.run_coroutine_threadsafe(ch.send("", delete_after=del_after, embed=blob), self.loop) + return + rsp = self.cmd.parseDiscord(ctx.formatted_message.page_prefix) + "\n" + if not self.text.strip_html_tags(ctx.formatted_message.msg).startswith(ctx.formatted_message.title): + rsp += f"**__{ctx.formatted_message.title}__**\n" + rsp += "> " + self.cmd.parseDiscord(ctx.formatted_message.msg) + rsp += f"\n {self.cmd.parseDiscord(ctx.formatted_message.page_postfix)}" + msg = rsp + else: + msg = self.cmd.parseDiscord(ctx.formatted_message) + msg: Message = asyncio.run_coroutine_threadsafe(ch.send(msg, delete_after=del_after), self.loop) async def discord_create_invite(self, reason=""): created_invite = await self.channel.create_invite(max_age=300, max_uses=2, reason=reason) @@ -578,28 +325,29 @@ class DiscordController: await discord_user.remove_roles(role) async def on_message(self, msg: Message): + if f"{msg.channel.id}" in self.setting_service.get_value("public_relay").lstrip("[").rstrip("]").split(','): + await msg.delete(delay=3600) if msg.author.id == self.client.user.id: return - if not self.setting_service.get_value("dc_relay_public"): - return channel: TextChannel = msg.channel - if channel.id == int(self.setting_service.get_value("dc_relay_public")): - - if self.setting_service.get_value('is_alliance_bot') == "0": - response = f"[{html.escape(msg.author.nick if msg.author.nick else msg.author.name, False)}" \ - f"]: " \ - f"{html.escape(emojis.decode(msg.clean_content), False)}" - else: - response = f"{html.escape(msg.author.nick if msg.author.nick else msg.author.name, False)}: " \ - f"{html.escape(emojis.decode(msg.clean_content), False)}" - await msg.delete(delay=3600) - sender = self.db.query_single("SELECT * from account a left join player p on a.main = p.char_id where a.discord_id = ?", [msg.author.id]) - self.relay_hub_service.send_message("public_relay", sender, html.escape(emojis.decode(msg.clean_content), False), response) + acc = self.data.get_account_discord_id(msg.author.id) + if acc: + acc.discord_nick = msg.author.nick or msg.author.name+'#'+msg.author.discriminator + prefix = f"[{self.alias_controller.get_alias(acc.member)}] {acc.name}: " if self.alias_usage().get_value() else f"[DC] {acc.name}: " + self.relay_hub_service.send_message("Discord_(" + channel.name+")", + DictObject(acc), msg.content, + prefix + msg.content) + else: + prefix = f"[-UNK-] {msg.author.username}: " if self.alias_usage().get_value() else f"[DC] {msg.author.username}: " + self.relay_hub_service.send_message("Discord_(" + channel.name+")", + DictObject({'char_id': 0, 'name': msg.author.username, 'discord_handle': f'{msg.author.name}#{msg.author.discriminator}'}), msg.content, + prefix + msg.content) + if str(channel.id) not in self.channel_blacklist().get_value().lstrip("[").rstrip("]").split(','): + self.bot.event_service.fire_event("discord_command", DictObject({'account': acc, 'message': msg})) if self.is_command(msg.content): admin = self.get_role("Administrator", self.guild.roles) council = self.get_role("Council", self.guild.roles) - if msg.content[:4] == "!pin": if admin in msg.author.roles: matches = re.findall(pattern="(.+?)<\/head>(.+)", string=msg.content[4:].strip(), @@ -611,7 +359,7 @@ class DiscordController: mess = await channel.send(embed=Embed(color=3066993, description=msg.content[4:])) await mess.pin() - if msg.content[:5] == "!note": + if msg.content[:6] == "!note ": if admin in msg.author.roles: matches = re.findall(pattern="(.+?)<\/head>(.+)", string=msg.content[5:].strip(), flags=re.DOTALL) @@ -636,73 +384,23 @@ class DiscordController: else: await msg.channel.purge(check=self.check, limit=count) await msg.delete(delay=0) - if msg.content[:10] == "!subscribe": - if admin in msg.author.roles: - out = msg.content[10:].strip() - if self.setting_service.get(out) is None: - await msg.reply(f"The Channel {out} does not exist.") - else: - self.setting_service.set_value(out, msg.channel.id) - self.send_message(msg.channel.id, Embed(color=3066993, title="Channel Guard", - description=f"This channel has been " - f"subscribed to the source {out}.")) - - def send_message(self, channel: int, message, delete=0): - if self.client.is_ready(): - channel: TextChannel = self.client.get_channel(int(channel)) if type(channel) in [int, str] else channel - if type(message) == Embed: - if delete != 0: - asyncio.run_coroutine_threadsafe(channel.send(embed=message, delete_after=delete), self.loop) - else: - asyncio.run_coroutine_threadsafe(channel.send(embed=message), self.loop) - - else: - if delete != 0: - asyncio.run_coroutine_threadsafe(channel.send(message, delete_after=delete), self.loop) - else: - asyncio.run_coroutine_threadsafe(channel.send(message), self.loop) def is_command(self, message: str): for x in ["subscribe", "pin", "purge", "note"]: if message.startswith("!" + x): return True - async def clean_channel(self): - if self.setting_service.get_value("dc_relay_public") not in ["0", None]: - channel: TextChannel = self.client.get_channel(int(self.setting_service.get_value("dc_relay_public"))) - for i in range(5): - await channel.purge(check=self.check) - def check(self, msg: Message): - if msg.pinned or len(msg.embeds) > 0: + if msg.pinned or (len(msg.embeds) > 0 and (msg.created_at.utcnow() < datetime.utcnow()-timedelta(hours=12))): return False else: return True - @event(event_type="main_changed", description="Fix discord names & ranks") - def fix_ranks(self, _, data): - row = self.db.query_single("SELECT * from account where char_id=?", [data.old_main_id]) - if row.discord_joined == 0: - self.logger.debug(f"{data.old_main_id} was not in discord, ignoring main fixing") - return - elif row.discord_joined == 1: - self.db.exec( - "UPDATE account set discord_handle=?, discord_id=?, discord_invite=?, discord_joined=? where char_id=?", - [row.discord_handle, row.discord_id, row.discord_invite, row.discord_joined, data.new_main_id]) - self.db.exec( - "UPDATE account set discord_handle='', discord_id=0, discord_invite='', discord_joined=0 " - "where char_id=?", - [data.old_main_id]) - self.logger.info(f"{data.old_main_id} was in discord, overwriting {data.new_main_id} with {data}") + def alias_usage(self) -> BooleanSettingType: + return self.setting_service.get("is_alliance_bot") - self.discord_update_account(data.new_main_id) + def channel_blacklist(self) -> TextSettingType: + return self.setting_service.get("channel_blacklist") - @timerevent(budatime="5m", description="update activity") - def change_count(self, _, _1): - if hasattr(self, "loop"): - count = self.db.query_single('SELECT count(*) as count from online ' - 'where char_id NOT IN (select char_id from org_bots) and bot=?', - [self.bot.get_char_id()]).count - act = discord.Activity(type=discord.ActivityType.listening, - name=f"{count} Players") - asyncio.run_coroutine_threadsafe(self.client.change_presence(activity=act), self.loop) + def public_relay(self) -> TextSettingType: + return self.setting_service.get("public_relay") diff --git a/modules/core/discord/discord_data.py b/modules/core/discord/discord_data.py new file mode 100644 index 0000000..274d8ac --- /dev/null +++ b/modules/core/discord/discord_data.py @@ -0,0 +1,25 @@ + +class DiscordData: + def __init__(self, db, client): + self.db = db + self.client = client + + def set_discord_invite(self, char_id, invite): + return self.db.exec("UPDATE account SET discord_invite = ? WHERE main = ?", [invite, char_id]) + + def set_discord_joined(self, char_id, handle, discord_id): + return self.db.exec("UPDATE account SET discord_joined = 1, discord_handle = ?, discord_id = ? WHERE main = ?", + [handle, discord_id, char_id]) + + def set_discord_left(self, char_id): + return self.db.exec("UPDATE account SET discord_joined = 0 WHERE main = ?", [char_id]) + + def set_discord_handle(self, discord_id, handle): + return self.db.exec("UPDATE account SET discord_handle = ? WHERE discord_id = ?", [handle, discord_id]) + + def get_discord_invite(self, invite): + return self.db.query_single("SELECT * FROM account WHERE discord_invite = ?", [invite]) + + def get_account_discord_id(self, discord_id): + return self.db.query_single( + "SELECT * FROM account a left join player p on a.char_id=p.char_id WHERE discord_id = ?", [discord_id]) \ No newline at end of file diff --git a/modules/core/discord/discord_management.py b/modules/core/discord/discord_management.py new file mode 100644 index 0000000..841ff77 --- /dev/null +++ b/modules/core/discord/discord_management.py @@ -0,0 +1,82 @@ +import asyncio + +from discord import Activity, ActivityType, TextChannel + +from core.dict_object import DictObject + + +class DiscordManager: + def __init__(self, client): + self.client = client + client = client.client + client.event(self.on_ready) + client.event(self.on_member_join) + client.event(self.on_member_remove) + client.event(self.on_member_update) + client.event(self.on_guild_role_delete) + client.event(self.on_guild_role_create) + client.event(self.on_guild_role_update) + client.event(self.on_invite_create) + client.event(self.on_invite_delete) + + async def on_member_update(self, member_before, member_after): + await self.client.discord_update_bot_basic() + + async def on_guild_role_create(self, role): + await self.client.discord_update_bot_full() + + async def on_guild_role_delete(self, role): + await self.client.discord_update_bot_full() + + async def on_guild_role_update(self, role_before, role_after): + await self.client.discord_update_bot_full() + + async def on_invite_create(self, invite): + await self.client.discord_update_bot_full() + + async def on_invite_delete(self, invite): + await self.client.discord_update_bot_full() + + async def on_member_join(self, member): + temp_invites = await self.client.guild.invites() + invite_used = self.discord_invite_used(temp_invites) + self.client.discord_queue.append( + DictObject({"type": "on_member_join", "member": member, "invite": invite_used})) + await invite_used.delete() + + def discord_invite_used(self, temp_invites): + for temp_invite in temp_invites: + # if temp_invite.inviter.id == self.client.user.id: + # ## Using this line limits it to invite created by the account the Discord bot runs as.. + for invite in self.client.invites: + if int(temp_invite.uses) > int(invite.uses) and temp_invite.code == invite.code: + return temp_invite + return None + + async def on_member_remove(self, member): + self.client.discord_queue.append(DictObject({"type": "on_member_remove", "member": member})) + await self.client.discord_update_bot_full() + + async def on_ready(self): + await self.client.discord_update_bot_full() + count = self.client.db.query_single('SELECT count(*) as count from online ' + 'where char_id NOT IN (select char_id from org_bots) and bot=?', + [self.client.bot.get_char_id()]).count + + act = Activity(type=ActivityType.listening, + name=f"{count} Players") + asyncio.run_coroutine_threadsafe(self.client.client.change_presence(activity=act), self.client.loop) + asyncio.run_coroutine_threadsafe(self.client.setup_relayhub(), self.client.loop) + await self.clean_channel() + + if self.client.guild.large: + self.client.logger.error( + f"Guild {self.client.guild.name} is classified as large, " + f"you need to request offline members to manage roles properly, this is not yet implemented") + + async def clean_channel(self): + for channel in self.client.setting_service.get_value("public_relay").lstrip("[").rstrip("]").split(','): + if ch := self.client.client.get_channel(int(channel)): + for i in range(5): + await ch.purge(check=self.client.check) + diff --git a/modules/core/private_channel/private_channel_controller.py b/modules/core/private_channel/private_channel_controller.py index ad3fa33..c3a2e76 100644 --- a/modules/core/private_channel/private_channel_controller.py +++ b/modules/core/private_channel/private_channel_controller.py @@ -235,22 +235,25 @@ class PrivateChannelController: @event(event_type=IgnCore.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT, description="Relay commands from the private channel to the relay hub", is_hidden=True) def outgoing_private_channel_message_event(self, _, event_data): + if isinstance(event_data.message, ChatBlob): pages = self.text.paginate(event_data.message, self.setting_service.get("org_channel_max_page_length").get_value()) if len(pages) < 4: for page in pages: - message = "{priv}{message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=page) + # message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=page) + event_data.message.page_prefix = self.PRIVATE_CHANNEL_PREFIX self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, page, - message) + event_data.message) else: - message = "{priv}{message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message.title) + # message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message.title) + event_data.message.page_prefix = self.PRIVATE_CHANNEL_PREFIX self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, event_data.message.title, - message) + event_data.message) else: message = "{priv}{message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message) self.message_hub_service.send_message(self.MESSAGE_SOURCE, diff --git a/modules/core/riadmin/riadmin_controller.py b/modules/core/riadmin/riadmin_controller.py index 363fdb9..6bf6899 100644 --- a/modules/core/riadmin/riadmin_controller.py +++ b/modules/core/riadmin/riadmin_controller.py @@ -1,20 +1,26 @@ import json import math +import typing from core.buddy_service import BuddyService from core.chat_blob import ChatBlob from core.command_param_types import Const, Character, Any, NamedParameters from core.decorators import instance, command, event from core.dict_object import DictObject + from core.logger import Logger -from core.lookup.character_service import CharacterService -from core.lookup.pork_service import PorkService -from core.private_channel_service import PrivateChannelService + from core.setting_service import SettingService from core.setting_types import TextSettingType from core.text import Text from core.translation_service import TranslationService from core.igncore import IgnCore +from core.private_channel_service import PrivateChannelService + +if typing.TYPE_CHECKING: + from core.lookup.character_service import CharacterService + from core.lookup.pork_service import PorkService + from core.event_service import EventService # noinspection SqlCaseVsIf,SqlCaseVsCoalesce @@ -38,6 +44,7 @@ class RIAdminController: self.getresp = self.ts.get_response self.buddy_service: BuddyService = registry.get_instance("buddy_service") self.pork: PorkService = registry.get_instance("pork_service") + self.event_service: EventService = registry.get_instance("event_service") def start(self): self.db.exec( @@ -54,6 +61,8 @@ class RIAdminController: TextSettingType(), "Allowed bots (charID's)", extended_description="This setting is *NOT* synchronized across the network;" " this needs to be done manually!") + def pre_start(self): + self.event_service.register_event_type("RAID_END") @event(event_type="buddy_logoff", description="Track raiders") def raider_logoff(self, _, event_data): diff --git a/modules/onlinebot/alliance/alliance_relay_controller.py b/modules/onlinebot/alliance/alliance_relay_controller.py index 5ba8517..6a19998 100644 --- a/modules/onlinebot/alliance/alliance_relay_controller.py +++ b/modules/onlinebot/alliance/alliance_relay_controller.py @@ -1,112 +1,112 @@ -from core.aochat import server_packets, client_packets -from core.conn import Conn -from core.decorators import instance -from core.igncore import IgnCore -from core.logger import Logger -from core.lookup.character_service import CharacterService -from core.setting_service import SettingService -from core.setting_types import TextSettingType, BooleanSettingType, ColorSettingType -from core.text import Text -from modules.onlinebot.online.org_alias_controller import OrgAliasController - - -@instance("AllianceRelayController") -class AllianceRelayController: - relay_channel_id = None - MESSAGE_SOURCE = "alliance" - - def __init__(self): - self.logger = Logger(__name__) - - # noinspection DuplicatedCode - def inject(self, registry): - self.bot: IgnCore = registry.get_instance("bot") - self.setting_service: SettingService = registry.get_instance("setting_service") - self.character_service: CharacterService = registry.get_instance("character_service") - self.message_hub_service = registry.get_instance("message_hub_service") - self.public_channel_service = registry.get_instance("public_channel_service") - self.text: Text = registry.get_instance("text") - self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller") - - def pre_start(self): - self.message_hub_service.register_message_source(self.MESSAGE_SOURCE) - - def start(self): - self.setting_service.register(self.module_name, "arelaybot", "", - TextSettingType(allow_empty=True), "Bot for alliance relay") - - self.setting_service.register(self.module_name, "arelay_enabled", False, - BooleanSettingType(), "Enable the alliance relay") - - self.setting_service.register(self.module_name, "arelay_color", "#C3C3C3", - ColorSettingType(), - "Color of messages from relay") - - self.message_hub_service.register_message_destination(self.MESSAGE_SOURCE, self.handle_relay_hub_message, [], - [self.MESSAGE_SOURCE]) - - self.bot.register_packet_handler(server_packets.PrivateChannelInvited.id, self.handle_private_channel_invite, - 100) - self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message) - - # noinspection DuplicatedCode - def handle_private_channel_invite(self, conn: Conn, packet: server_packets.PrivateChannelInvited): - if conn.id != "main": - return - - if not self.setting_service.get("arelay_enabled").get_value(): - return - - channel_name = self.character_service.get_char_name(packet.private_channel_id) - if self.setting_service.get_value("arelaybot").lower() == channel_name.lower(): - self.bot.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id)) - self.logger.info("Joined private channel {channel}".format(channel=channel_name)) - self.relay_channel_id = packet.private_channel_id - - # noinspection DuplicatedCode - def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage): - if conn.id != "main": - return - - if not self.setting_service.get("arelay_enabled").get_value(): - return - - # ignore packets from the bot's own private channel and from the bot itself - if packet.private_channel_id == self.bot.get_char_id() or packet.char_id == self.bot.get_char_id(): - return - - message = packet.message.lstrip() - if message[:6] != "!agcr ": - return - - message = message[6:] - formatted_message = self.setting_service.get("arelay_color").format_text(message) - - sender = None - self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, message, formatted_message) - - def handle_relay_hub_message(self, ctx): - if not self.setting_service.get("arelay_enabled").get_value(): - return - - plain_msg = ctx.message or ctx.formatted_message - invite = self.text.make_chatcmd("click here", "/tell discord invite", - style="style='text-decoration:none'") - name = f"[{self.alias_controller.get_alias(ctx.sender.org_id)}] {ctx.sender.name}" - blob = self.text.format_page('Info', - f"
::: Information :::


" - f"This message has been sent to you by:

" - f"Igncom
" - f"{ctx.sender.discord_handle}
" - f"{name} on Alliance Discord.

" - f"To reply, either respond in the relay or " - f"contact them directly at the provided handles.

" - f"Have you joined The Alliance Discord yet? " - f"If not {invite} to receive an invite.") - self.send_message_to_alliance(f"{name}: {plain_msg}" + f" [{blob}]") - - def send_message_to_alliance(self, msg): - if self.relay_channel_id: - packet = client_packets.PrivateChannelMessage(self.relay_channel_id, - "!agcr " + self.text.format_message(msg, False), "\0") - self.bot.conns["main"].send_packet(packet) +# from core.aochat import server_packets, client_packets +# from core.conn import Conn +# from core.decorators import instance +# from core.igncore import IgnCore +# from core.logger import Logger +# from core.lookup.character_service import CharacterService +# from core.setting_service import SettingService +# from core.setting_types import TextSettingType, BooleanSettingType, ColorSettingType +# from core.text import Text +# from modules.onlinebot.online.org_alias_controller import OrgAliasController +# +# +# @instance("AllianceRelayController") +# class AllianceRelayController: +# relay_channel_id = None +# MESSAGE_SOURCE = "alliance" +# +# def __init__(self): +# self.logger = Logger(__name__) +# +# # noinspection DuplicatedCode +# def inject(self, registry): +# self.bot: IgnCore = registry.get_instance("bot") +# self.setting_service: SettingService = registry.get_instance("setting_service") +# self.character_service: CharacterService = registry.get_instance("character_service") +# self.message_hub_service = registry.get_instance("message_hub_service") +# self.public_channel_service = registry.get_instance("public_channel_service") +# self.text: Text = registry.get_instance("text") +# self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller") +# +# def pre_start(self): +# self.message_hub_service.register_message_source(self.MESSAGE_SOURCE) +# +# def start(self): +# self.setting_service.register(self.module_name, "arelaybot", "", +# TextSettingType(allow_empty=True), "Bot for alliance relay") +# +# self.setting_service.register(self.module_name, "arelay_enabled", False, +# BooleanSettingType(), "Enable the alliance relay") +# +# self.setting_service.register(self.module_name, "arelay_color", "#C3C3C3", +# ColorSettingType(), +# "Color of messages from relay") +# +# # self.message_hub_service.register_message_destination(self.MESSAGE_SOURCE, self.handle_relay_hub_message, [], +# # [self.MESSAGE_SOURCE]) +# +# self.bot.register_packet_handler(server_packets.PrivateChannelInvited.id, self.handle_private_channel_invite, +# 100) +# self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message) +# +# # noinspection DuplicatedCode +# def handle_private_channel_invite(self, conn: Conn, packet: server_packets.PrivateChannelInvited): +# if conn.id != "main": +# return +# +# if not self.setting_service.get("arelay_enabled").get_value(): +# return +# +# channel_name = self.character_service.get_char_name(packet.private_channel_id) +# if self.setting_service.get_value("arelaybot").lower() == channel_name.lower(): +# self.bot.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id)) +# self.logger.info("Joined private channel {channel}".format(channel=channel_name)) +# self.relay_channel_id = packet.private_channel_id +# +# # noinspection DuplicatedCode +# def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage): +# if conn.id != "main": +# return +# +# if not self.setting_service.get("arelay_enabled").get_value(): +# return +# +# # ignore packets from the bot's own private channel and from the bot itself +# if packet.private_channel_id == self.bot.get_char_id() or packet.char_id == self.bot.get_char_id(): +# return +# +# message = packet.message.lstrip() +# if message[:6] != "!agcr ": +# return +# +# message = message[6:] +# formatted_message = self.setting_service.get("arelay_color").format_text(message) +# +# sender = None +# self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, message, formatted_message) +# +# def handle_relay_hub_message(self, ctx): +# if not self.setting_service.get("arelay_enabled").get_value(): +# return +# +# plain_msg = ctx.message or ctx.formatted_message +# invite = self.text.make_chatcmd("click here", "/tell discord invite", +# style="style='text-decoration:none'") +# name = f"[{self.alias_controller.get_alias(ctx.sender.org_id)}] {ctx.sender.name}" +# blob = self.text.format_page('Info', +# f"
::: Information :::


" +# f"This message has been sent to you by:

" +# f"Igncom
" +# f"{ctx.sender.discord_handle}
" +# f"{name} on Alliance Discord.

" +# f"To reply, either respond in the relay or " +# f"contact them directly at the provided handles.

" +# f"Have you joined The Alliance Discord yet? " +# f"If not {invite} to receive an invite.") +# self.send_message_to_alliance(f"{name}: {plain_msg}" + f" [{blob}]") +# +# def send_message_to_alliance(self, msg): +# if self.relay_channel_id: +# packet = client_packets.PrivateChannelMessage(self.relay_channel_id, +# "!agcr " + self.text.format_message(msg, False), "\0") +# self.bot.conns["main"].send_packet(packet) diff --git a/modules/onlinebot/online/org_controller.py b/modules/onlinebot/online/org_controller.py index cae60ba..2889bf2 100644 --- a/modules/onlinebot/online/org_controller.py +++ b/modules/onlinebot/online/org_controller.py @@ -3,6 +3,7 @@ import re import time from threading import Thread +import datetime import requests from core.buddy_service import BuddyService @@ -359,4 +360,8 @@ class OrgController: if len(current) > 10: out.append(current) if len(out) > 0: - self.relay_hub_service.send_message("member_logger", None, out, out) + y = 0 + for x in out: + y += 1 + blob = ChatBlob(f"Recent changes - {datetime.date.today()} - ({y}/{len(out)})", x, embed=True) + self.relay_hub_service.send_message("member_logger", None, blob, blob) diff --git a/modules/orgbot/org/org_controller.py b/modules/orgbot/org/org_controller.py index 34440ea..1adedb5 100644 --- a/modules/orgbot/org/org_controller.py +++ b/modules/orgbot/org/org_controller.py @@ -120,17 +120,19 @@ class OrgChannelController: self.setting_service.get("org_channel_max_page_length").get_value()) if len(pages) < 4: for page in pages: - message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=page) + # message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=page) + event_data.message.page_prefix = self.ORG_CHANNEL_PREFIX self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, page, - message) + event_data.message) else: - message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message.title) + # message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message.title) + event_data.message.page_prefix = self.ORG_CHANNEL_PREFIX self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, event_data.message.title, - message) + event_data.message) else: message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message) self.message_hub_service.send_message(self.MESSAGE_SOURCE, diff --git a/modules/raidbot/raid/raidbot_controller.py b/modules/raidbot/raid/raidbot_controller.py index c792aa8..fe4a00a 100644 --- a/modules/raidbot/raid/raidbot_controller.py +++ b/modules/raidbot/raid/raidbot_controller.py @@ -76,9 +76,6 @@ class RaidbotController(BaseModule): self.account_service: AccountService = registry.get_instance("account_service") self.buddy_service: BuddyService = registry.get_instance("buddy_service") - def pre_start(self): - self.event_service.register_event_type("RAID_END") - @event("connect", "Adds all raiders to buddylist") def connect(self, _, _1): query = self.db.query("SELECT char_id, member from account where member != -1 and disabled = 0") diff --git a/modules/standard/MessageDistributor/message_distributor.py b/modules/standard/MessageDistributor/message_distributor.py index 26cf88e..42806f2 100644 --- a/modules/standard/MessageDistributor/message_distributor.py +++ b/modules/standard/MessageDistributor/message_distributor.py @@ -1,137 +1,137 @@ -import asyncio -import datetime -import html - -# noinspection PyPackageRequirements -from discord import Embed - -from core.command_service import CommandService -from core.db import DB -from core.decorators import instance -from core.logger import Logger -from core.lookup.character_service import CharacterService -from core.lookup.pork_service import PorkService -from core.message_hub_service import MessageHubService -from core.registry import Registry -from core.setting_service import SettingService -from core.setting_types import NumberSettingType -from core.text import Text -from core.igncore import IgnCore -from modules.core.accounting.services.access_service import AccessService -from modules.core.discord.discord_controller import DiscordController - - -# noinspection DuplicatedCode,PyAttributeOutsideInit -@instance() -class MessageDistributor: - MESSAGE_SOURCE = "discord" - - def __init__(self): - self.logger = Logger(__name__) - - def inject(self, registry): - self.bot: IgnCore = registry.get_instance("bot") - self.db: DB = registry.get_instance("db") - self.character_service: CharacterService = registry.get_instance("character_service") - self.access_service: AccessService = registry.get_instance("access_service") - self.pork: PorkService = registry.get_instance("pork_service") - self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service") - self.discord: DiscordController = registry.get_instance("discord_controller") - self.cmd_service: CommandService = registry.get_instance("command_service") - self.setting_service: SettingService = registry.get_instance("setting_service") - self.text: Text = registry.get_instance("text") - - def pre_start(self): - self.relay_hub_service.register_message_source("public_relay") - self.relay_hub_service.register_message_source("tell_logger") - self.relay_hub_service.register_message_source("system_logger") - self.relay_hub_service.register_message_source("access_denied_logger") - self.relay_hub_service.register_message_source("member_logger") - - def start(self): - self.relay_hub_service.register_message_destination("tell_log", self.handle_logging_tell, ["tell_logger"], - ["tell_log"]) - self.setting_service.register(self.module_name, "dc_tell_log", 0, NumberSettingType(allow_empty=True), - "ChannelID for the Tell Log") - - self.relay_hub_service.register_message_destination("access_denied_log", self.handle_logging_denied, - ["access_denied_logger"], ["access_denied_log"]) - self.setting_service.register(self.module_name, "dc_denied_log", 0, NumberSettingType(allow_empty=True), - "ChannelID for the Access Denied Log") - - self.relay_hub_service.register_message_destination("relay_log", self.handle_logging_relay, - ["alliance", "public_relay"], ["relay_log"]) - self.setting_service.register(self.module_name, "dc_relay_log", 0, NumberSettingType(allow_empty=True), - "ChannelID for the Relay Log") - - self.relay_hub_service.register_message_destination("public_relay", self.handle_public_relay, ["alliance"], - ["public_relay"]) - self.setting_service.register(self.module_name, "dc_relay_public", 0, NumberSettingType(allow_empty=True), - "ChannelID for the Public Relay Channel") - - self.relay_hub_service.register_message_destination("system", self.handle_logging_system, ["system_logger"], - ["system"]) - self.setting_service.register(self.module_name, "dc_system_log", 0, NumberSettingType(allow_empty=True), - "ChannelID for the System Channel") - - self.relay_hub_service.register_message_destination("dc_member_log", self.handle_logging_members, - ["member_logger"], ["dc_member_log"]) - self.setting_service.register(self.module_name, "dc_member_log", 0, NumberSettingType(allow_empty=True), - "ChannelID for the Member Log") - - if Registry.get_instance("darknet", is_optional=True): - self.relay_hub_service.register_message_destination("dc_darknet_log", self.handle_darknet_log, ["darknet"], - [""]) - self.setting_service.register(self.module_name, "dc_darknet_log", 0, NumberSettingType(allow_empty=True), - "ChannelID for the Darknet Log") - - def handle_public_relay(self, ctx): - if self.setting_service.get_value("dc_relay_public") != "0": - message = self.format(ctx.formatted_message) - self.discord.send_message(self.setting_service.get_value("dc_relay_public"), html.unescape(message), - delete=3600) # delete after 1h - - def handle_logging_tell(self, ctx): - if self.setting_service.get_value("dc_tell_log") != "0": - message = self.format(ctx.message) - self.discord.send_message(self.setting_service.get_value("dc_tell_log"), - f"[FROM] {self.character_service.get_char_name(ctx.sender)}: {message}") - - def handle_logging_denied(self, ctx): - if self.setting_service.get_value("dc_denied_log") != "0": - message = self.format(ctx.message) - self.discord.send_message(self.setting_service.get_value("dc_denied_log"), message) - - def handle_darknet_log(self, ctx): - if self.setting_service.get_value("dc_darknet_log") != "0": - message = self.format(ctx.message) - self.discord.send_message(self.setting_service.get_value("dc_darknet_log"), message) - - def handle_logging_relay(self, ctx): - if self.setting_service.get_value("dc_relay_log") != "0": - message = self.format(ctx.formatted_message) - self.discord.send_message(self.setting_service.get_value("dc_relay_log"), f"{html.unescape(message)}") - - def handle_logging_system(self, ctx): - if self.setting_service.get_value("dc_system_log") != "0": - message = self.format(ctx.message) - self.discord.send_message(self.setting_service.get_value("dc_system_log"), f"{html.unescape(message)}") - - def handle_logging_members(self, ctx): - if self.setting_service.get_value("dc_member_log") != "0": - channel = self.discord.client.get_channel(int(self.setting_service.get_value("dc_member_log"))) - spam = [] - current = 0 - for message in ctx.message: - current += 1 - spam.append(Embed(title=f"Recent changes - {datetime.date.today()} - ({current}/{len(ctx.message)})", - description=message, color=3066993)) - asyncio.run_coroutine_threadsafe(self.send_spam(channel, spam), self.discord.loop) - - async def send_spam(self, channel, message): - if self.discord.client.is_ready(): - for i in message: - await channel.send(embed=i) - - def format(self, message): - return self.text.strip_html_tags(message) +# import asyncio +# import datetime +# import html +# +# # noinspection PyPackageRequirements +# from discord import Embed +# +# from core.command_service import CommandService +# from core.db import DB +# from core.decorators import instance +# from core.logger import Logger +# from core.lookup.character_service import CharacterService +# from core.lookup.pork_service import PorkService +# from core.message_hub_service import MessageHubService +# from core.registry import Registry +# from core.setting_service import SettingService +# from core.setting_types import NumberSettingType +# from core.text import Text +# from core.igncore import IgnCore +# from modules.core.accounting.services.access_service import AccessService +# from modules.core.discord.discord_controller import DiscordController +# +# +# # noinspection DuplicatedCode,PyAttributeOutsideInit +# @instance() +# class MessageDistributor: +# MESSAGE_SOURCE = "discord" +# +# def __init__(self): +# self.logger = Logger(__name__) +# +# def inject(self, registry): +# self.bot: IgnCore = registry.get_instance("bot") +# self.db: DB = registry.get_instance("db") +# self.character_service: CharacterService = registry.get_instance("character_service") +# self.access_service: AccessService = registry.get_instance("access_service") +# self.pork: PorkService = registry.get_instance("pork_service") +# self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service") +# self.discord: DiscordController = registry.get_instance("discord_controller") +# self.cmd_service: CommandService = registry.get_instance("command_service") +# self.setting_service: SettingService = registry.get_instance("setting_service") +# self.text: Text = registry.get_instance("text") +# +# def pre_start(self): +# self.relay_hub_service.register_message_source("public_relay") +# self.relay_hub_service.register_message_source("tell_logger") +# self.relay_hub_service.register_message_source("system_logger") +# self.relay_hub_service.register_message_source("access_denied_logger") +# self.relay_hub_service.register_message_source("member_logger") +# +# def start(self): +# self.relay_hub_service.register_message_destination("tell_log", self.handle_logging_tell, ["tell_logger"], +# ["tell_log"]) +# self.setting_service.register(self.module_name, "dc_tell_log", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the Tell Log") +# +# self.relay_hub_service.register_message_destination("access_denied_log", self.handle_logging_denied, +# ["access_denied_logger"], ["access_denied_log"]) +# self.setting_service.register(self.module_name, "dc_denied_log", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the Access Denied Log") +# +# self.relay_hub_service.register_message_destination("relay_log", self.handle_logging_relay, +# ["alliance", "public_relay"], ["relay_log"]) +# self.setting_service.register(self.module_name, "dc_relay_log", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the Relay Log") +# +# self.relay_hub_service.register_message_destination("public_relay", self.handle_public_relay, ["alliance"], +# ["public_relay"]) +# self.setting_service.register(self.module_name, "dc_relay_public", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the Public Relay Channel") +# +# self.relay_hub_service.register_message_destination("system", self.handle_logging_system, ["system_logger"], +# ["system"]) +# self.setting_service.register(self.module_name, "dc_system_log", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the System Channel") +# +# self.relay_hub_service.register_message_destination("dc_member_log", self.handle_logging_members, +# ["member_logger"], ["dc_member_log"]) +# self.setting_service.register(self.module_name, "dc_member_log", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the Member Log") +# +# if Registry.get_instance("darknet", is_optional=True): +# self.relay_hub_service.register_message_destination("dc_darknet_log", self.handle_darknet_log, ["darknet"], +# [""]) +# self.setting_service.register(self.module_name, "dc_darknet_log", 0, NumberSettingType(allow_empty=True), +# "ChannelID for the Darknet Log") +# +# def handle_public_relay(self, ctx): +# if self.setting_service.get_value("dc_relay_public") != "0": +# message = self.format(ctx.formatted_message) +# self.discord.send_message(self.setting_service.get_value("dc_relay_public"), html.unescape(message), +# delete=3600) # delete after 1h +# +# def handle_logging_tell(self, ctx): +# if self.setting_service.get_value("dc_tell_log") != "0": +# message = self.format(ctx.message) +# self.discord.send_message(self.setting_service.get_value("dc_tell_log"), +# f"[FROM] {self.character_service.get_char_name(ctx.sender)}: {message}") +# +# def handle_logging_denied(self, ctx): +# if self.setting_service.get_value("dc_denied_log") != "0": +# message = self.format(ctx.message) +# self.discord.send_message(self.setting_service.get_value("dc_denied_log"), message) +# +# def handle_darknet_log(self, ctx): +# if self.setting_service.get_value("dc_darknet_log") != "0": +# message = self.format(ctx.message) +# self.discord.send_message(self.setting_service.get_value("dc_darknet_log"), message) +# +# def handle_logging_relay(self, ctx): +# if self.setting_service.get_value("dc_relay_log") != "0": +# message = self.format(ctx.formatted_message) +# self.discord.send_message(self.setting_service.get_value("dc_relay_log"), f"{html.unescape(message)}") +# +# def handle_logging_system(self, ctx): +# if self.setting_service.get_value("dc_system_log") != "0": +# message = self.format(ctx.message) +# self.discord.send_message(self.setting_service.get_value("dc_system_log"), f"{html.unescape(message)}") +# +# def handle_logging_members(self, ctx): +# if self.setting_service.get_value("dc_member_log") != "0": +# channel = self.discord.client.get_channel(int(self.setting_service.get_value("dc_member_log"))) +# spam = [] +# current = 0 +# for message in ctx.message: +# current += 1 +# spam.append(Embed(title=f"Recent changes - {datetime.date.today()} - ({current}/{len(ctx.message)})", +# description=message, color=3066993)) +# asyncio.run_coroutine_threadsafe(self.send_spam(channel, spam), self.discord.loop) +# +# async def send_spam(self, channel, message): +# if self.discord.client.is_ready(): +# for i in message: +# await channel.send(embed=i) +# +# def format(self, message): +# return self.text.strip_html_tags(message) diff --git a/modules/orgbot/alliance/alliance_relay.py b/modules/standard/alliance/alliance_relay.py similarity index 81% rename from modules/orgbot/alliance/alliance_relay.py rename to modules/standard/alliance/alliance_relay.py index 41b7f33..d9474cb 100644 --- a/modules/orgbot/alliance/alliance_relay.py +++ b/modules/standard/alliance/alliance_relay.py @@ -1,4 +1,5 @@ import re +import typing from core.aochat import server_packets, client_packets from core.aochat.client_packets import PrivateChannelLeave @@ -16,6 +17,10 @@ from core.igncore import IgnCore # noinspection DuplicatedCode +if typing.TYPE_CHECKING: + from modules.core.discord.discord_controller import DiscordController + + @instance() class AllianceRelay: MESSAGE_SOURCE = "alliance" @@ -30,6 +35,7 @@ class AllianceRelay: self.character_service: CharacterService = registry.get_instance("character_service") self.message_hub_service = registry.get_instance("message_hub_service") self.public_channel_service = registry.get_instance("public_channel_service") + self.discord: DiscordController = registry.get_instance("discord_controller") self.text: Text = registry.get_instance("text") def pre_start(self): @@ -54,17 +60,24 @@ class AllianceRelay: if color: return f"{msg}" return "UNSET" + blob = "" for bot in self.relay_bots().get_value().keys(): - enabled = self.relay_enabled().get_value().get(bot, self.relay_enabled().get_value().get('default', 'UNSET')) + enabled = self.relay_enabled().get_value().get(bot, + self.relay_enabled().get_value().get('default', 'UNSET')) prefix = self.relay_symbols().get_value().get(bot, self.relay_symbols().get_value().get('default')) relay_cmd = self.relay_command().get_value().get(bot, self.relay_command().get_value().get('default')) - base = self.relay_color_base().get_value().get(bot, self.relay_color_base().get_value().get('default', None)) - sender = self.relay_color_sender().get_value().get(bot, self.relay_color_sender().get_value().get('default', None)) + base = self.relay_color_base().get_value().get(bot, + self.relay_color_base().get_value().get('default', None)) + sender = self.relay_color_sender().get_value().get(bot, self.relay_color_sender().get_value().get('default', + None)) abbrv = self.relay_color_org().get_value().get(bot, self.relay_color_org().get_value().get('default', None)) msg = self.relay_color_msg().get_value().get(bot, self.relay_color_msg().get_value().get('default', None)) - our_abbrv = self.relay_guild_abbreviations().get_value().get(bot, self.relay_guild_abbreviations().get_value().get('default', 'UNSET')) - example = display_color(base, f'[{display_color(abbrv, our_abbrv)}] {display_color(sender, request.sender.name)}: {display_color(msg, "And I sent you a message")}') + our_abbrv = self.relay_guild_abbreviations().get_value().get(bot, + self.relay_guild_abbreviations().get_value().get( + 'default', 'UNSET')) + example = display_color(base, + f'[{display_color(abbrv, our_abbrv)}] {display_color(sender, request.sender.name)}: {display_color(msg, "And I sent you a message")}') blob += f"Relay {self.character_service.get_char_name(int(bot))}:\n" blob += f" Enabled: {enabled} [{self.text.make_tellcmd('Toggle', f'mrelay status {bot} {not enabled}')}]\n" blob += f" Prefix: {prefix} [{self.text.make_tellcmd('Edit', f'mrelay prefix {bot}')}]\n" @@ -148,7 +161,8 @@ class AllianceRelay: return f"Successfully deleted relay {bot.name}" @command(command="mrelay", - params=[Const("rcmd"), Character("relaybot_name"), Any("relay_command", is_optional=True)], access_level="admin", + params=[Const("rcmd"), Character("relaybot_name"), Any("relay_command", is_optional=True)], + access_level="admin", description="Change the relay command used in a relay") def mrelay_rcmd(self, request, _, bot, relay_command): bots: dict = self.relay_bots().get_value() @@ -173,7 +187,8 @@ class AllianceRelay: f"{self.character_service.get_char_name(int(bot))}: {relay_command}" @command(command="mrelay", - params=[Const("prefix"), Character("relaybot_name"), Any("prefix", is_optional=True)], access_level="admin", + params=[Const("prefix"), Character("relaybot_name"), Any("prefix", is_optional=True)], + access_level="admin", description="Change the prefix setting of the relays") def mrelay_prefix(self, request, _, bot, prefix): bots: dict = self.relay_bots().get_value() @@ -234,7 +249,9 @@ class AllianceRelay: return f"Successfully changed the status of the relay " \ f"{self.character_service.get_char_name(int(bot))} to: {option}" - @command(command="mrelay", params=[Const("color"), Character("relaybot_name"), Options(["base", "sender", "org", "msg"]), Any("color_code", is_optional=True)], access_level="admin", + @command(command="mrelay", + params=[Const("color"), Character("relaybot_name"), Options(["base", "sender", "org", "msg"]), + Any("color_code", is_optional=True)], access_level="admin", description="Change the color settings of the relays") def mrelay_color(self, request, _, bot, option, color): bots: dict = self.relay_bots().get_value() @@ -312,7 +329,8 @@ class AllianceRelay: return if str(packet.private_channel_id) not in self.relay_bots().get_value().keys(): return - if not self.relay_enabled().get_value().get(str(packet.private_channel_id), self.relay_enabled().get_value().get('default', False)): + if not self.relay_enabled().get_value().get(str(packet.private_channel_id), + self.relay_enabled().get_value().get('default', False)): return self.bot.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id)) self.logger.info(f"Joined private channel {self.character_service.get_char_name(packet.private_channel_id)}") @@ -344,38 +362,72 @@ class AllianceRelay: text = text.strip() plain = f"[{org}] {name}: {text}" - org = self.format_text(self.relay_color_org().get_value().get(priv, self.relay_color_org().get_value().get("default")), org) - name = self.format_text(self.relay_color_sender().get_value().get(priv, self.relay_color_sender().get_value().get("default")), name) - text = self.format_text(self.relay_color_msg().get_value().get(priv, self.relay_color_msg().get_value().get("default")), text) - formatted = self.format_text(self.relay_color_base().get_value().get(priv, self.relay_color_base().get_value().get("default")), f"[{org}] {name}: {text}") + org = self.format_text( + self.relay_color_org().get_value().get(priv, self.relay_color_org().get_value().get("default")), org) + name = self.format_text( + self.relay_color_sender().get_value().get(priv, self.relay_color_sender().get_value().get("default")), name) + text = self.format_text( + self.relay_color_msg().get_value().get(priv, self.relay_color_msg().get_value().get("default")), text) + formatted = self.format_text( + self.relay_color_base().get_value().get(priv, self.relay_color_base().get_value().get("default")), + f"[{org}] {name}: {text}") sender = DictObject({"char_id": packet.char_id, "name": self.character_service.get_char_name(packet.char_id)}) - self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, plain, formatted) + self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, self.text.strip_html_tags(plain), formatted) def handle_relay_hub_message(self, ctx): - enabled = lambda x: self.relay_enabled().get_value().get(x, self.relay_enabled().get_value().get('default', False)) + enabled = lambda x: self.relay_enabled().get_value().get(x, + self.relay_enabled().get_value().get('default', False)) if not ctx.message: return + if not ctx.sender: + return + if type(ctx.message) == ChatBlob: + return for key in self.relay_bots().get_value().keys(): if key not in self.relay_channel: continue if not enabled(key): continue prefix = self.relay_symbols().get_value().get(key, self.relay_symbols().get_value().get('default', False)) - if not ctx.message.startswith(prefix): + if not(ctx.message.startswith(prefix)): continue - + sender = ctx.sender.name message = ctx.message[len(prefix):].strip() if len(message) < 1: continue - sender = ctx.sender.name abbrv = self.relay_guild_abbreviations().get_value().get(key, self.setting_service.get_value("org_name")) if ctx.source == "private_channel": abbrv += " - Guest" - elif ctx.source == "public_relay": + elif ctx.source.startswith("Discord_("): abbrv += " - DC" + cmd = self.relay_command().get_value().get(key, self.relay_command().get_value().get("default")) - msg = f"{cmd} [{abbrv}] {sender}: {message}" + # msg = f"{cmd} [{abbrv}] {sender}: {message}" + blob = "" + if ctx.source.startswith("Discord_("): + if self.discord.alias_usage().get_value(): + abbrv = self.discord.alias_controller.get_alias(ctx.sender.org_id) + invite = self.text.make_chatcmd("click here", "/tell discord invite", + style="style='text-decoration:none'") + # name = "" + if self.discord.guild and self.discord.bot: + name = f"DC Server: {self.discord.guild.name} - " \ + f"Bot: " + blob = self.text.format_page('Info', + f"
::: Information :::


" + f"This message has been sent to you by:

" + f"{name}
" + f"Sender: {ctx.sender.name}
" + f"Tag: {ctx.sender.discord_nick}

" + f"To reply, either respond in the relay or " + f"contact them directly at the provided handles.

" + f"Have you joined our Discord Server yet? " + f"If not {invite} to receive an invite.") + blob = f"[{blob}]" + # msg = f"{cmd} [{self.discord.alias_controller.get_alias(ctx.sender.org_id)}] {ctx.message} [{blob}]" + msg = f"{cmd} [{abbrv}] {sender}: {message} {blob}" + self.send_message_to_alliance(key, msg) def send_message_to_alliance(self, alliance, msg): @@ -398,7 +450,8 @@ class AllianceRelay: def relay_enabled(self) -> DictionarySettingType: return DictionarySettingType() - @setting(name="relay_guild_abbreviations", value={"default": "UNSET"}, description="Abbreviations used for the relays") + @setting(name="relay_guild_abbreviations", value={"default": "UNSET"}, + description="Abbreviations used for the relays") def relay_guild_abbreviations(self) -> DictionarySettingType: return DictionarySettingType() @@ -431,3 +484,6 @@ class AllianceRelay: def format_text(self, color, message): return f"{message}" + + # def alias_usage(self): + # return self.setting_service.get("use_prefix") diff --git a/modules/standard/helpbot/time_controller.py b/modules/standard/helpbot/time_controller.py index 445b6b5..70edb62 100644 --- a/modules/standard/helpbot/time_controller.py +++ b/modules/standard/helpbot/time_controller.py @@ -47,7 +47,7 @@ class TimeController: day += "rd" else: day += "th" - return f"Currently its {now.hour}:{now.minute}:{now.second} " \ + return f"Currently its {now.hour:02}:{now.minute:02}:{now.second:02} " \ f"{calendar.month_name[now.month]} {day}, {now.year + 27474} Rubi-Ka Universal Time." @command(command="time", params=[Any("timezone")], access_level="member", diff --git a/modules/standard/news/weekly_controller.py b/modules/standard/news/weekly_controller.py index b3124ad..1d22ff4 100644 --- a/modules/standard/news/weekly_controller.py +++ b/modules/standard/news/weekly_controller.py @@ -1,4 +1,5 @@ import time +import typing from datetime import datetime from core.buddy_service import BuddyService @@ -8,16 +9,17 @@ from core.db import DB from core.decorators import instance, command from core.job_scheduler import JobScheduler from core.logger import Logger -from core.lookup.pork_service import PorkService -from core.setting_service import SettingService from core.text import Text from core.igncore import IgnCore from core.util import Util -from modules.core.accounting.preference_controller import PreferenceController -from modules.core.accounting.services.account_service import AccountService -from modules.core.ban.ban_service import BanService -from modules.core.discord.discord_controller import DiscordController -from modules.onlinebot.online.org_alias_controller import OrgAliasController +if typing.TYPE_CHECKING: + from core.lookup.pork_service import PorkService + from core.setting_service import SettingService + from modules.core.accounting.preference_controller import PreferenceController + from modules.core.accounting.services.account_service import AccountService + from modules.core.ban.ban_service import BanService + from modules.core.discord.discord_controller import DiscordController + from modules.onlinebot.online.org_alias_controller import OrgAliasController @instance() diff --git a/modules/standard/news/worldboss_controller.py b/modules/standard/news/worldboss_controller.py index db628b9..51858ba 100644 --- a/modules/standard/news/worldboss_controller.py +++ b/modules/standard/news/worldboss_controller.py @@ -3,13 +3,14 @@ import time from core.chat_blob import ChatBlob from core.command_alias_service import CommandAliasService from core.command_param_types import Any -from core.decorators import instance, command, event +from core.decorators import instance, command, event, setting from core.dict_object import DictObject from core.igncore import IgnCore from core.job_scheduler import JobScheduler from core.logger import Logger +from core.message_hub_service import MessageHubService from core.setting_service import SettingService -from core.setting_types import BooleanSettingType +from core.setting_types import BooleanSettingType, TextSettingType from core.text import Text from core.util import Util from modules.standard.datanet.ws_controller import WebsocketRelayController @@ -35,8 +36,11 @@ class WorldBossController: self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service") self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler") self.setting_service: SettingService = registry.get_instance("setting_service") + self.relay_hub: MessageHubService = registry.get_instance("message_hub_service") def pre_start(self): + self.relay_hub.register_message_source("timers") + self.setting_service.register(self.module_name, 'timer_spam', False, BooleanSettingType(), "should timers be spammed") self.command_alias_service.add_alias("tara", "wb tara") @@ -120,7 +124,7 @@ class WorldBossController: return DictObject({'spawn': last - immortal, 'mortal': last}) def send_warn(self, msg): - self.bot.send_private_channel_message(f"[WB] {msg}") + self.relay_hub.send_message("timers", None, f"[WB] {msg}", f"[WB] {msg}") def get_next_alert(self, duration): for alert in self.alerts: @@ -190,3 +194,8 @@ class WorldBossController: job['id'] = job_id return self.jobs.append({'name': timer.name, 'id': job_id}) + + # @setting('alert_times', '[480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15, 60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0]', 'Worldboss timer spam messages (ETA and actual)') + # def alert_times(self): + # return TextSettingType(['[480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15, 60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0]']) + diff --git a/modules/standard/online/online_display.py b/modules/standard/online/online_display.py index 59879a8..d7f15b8 100644 --- a/modules/standard/online/online_display.py +++ b/modules/standard/online/online_display.py @@ -39,11 +39,11 @@ class OnlineDisplay: if org > 0: postfix.append(f"Org: {org}") if priv > 0: - postfix.append(f"Priv: {priv}") + postfix.append(f"Priv: {priv}") if notify > 0: postfix.append(f"Buddylist: {notify}") blob = ChatBlob(title, blob, suffix=f" ({f', '.join(postfix)})") - return blob + return blob if (org+priv+notify) != 0 else "There's nobody online." def format_by_channel_main(self, query, params): query += "order by channel_id, main_name, p.name=main_name, p.name, r_id" @@ -161,7 +161,7 @@ class OnlineDisplay: if org_id != temp_blob[key]["id"]: org_id = temp_blob[key]["id"] blob += f"\n{temp_blob[key]['name']}" \ - f"
({len(temp_blob[key]['online']): >3} " \ + f"
({len(temp_blob[key]['online']): >3} " \ f"| {len(temp_blob[key]['online']) / len(query) * 100:.2f}%)
\n" blob += self.format_org(user) return blob, 0, 0, len(query) diff --git a/modules/standard/specials/specials_controller.py b/modules/standard/specials/specials_controller.py index ab003cf..171b433 100644 --- a/modules/standard/specials/specials_controller.py +++ b/modules/standard/specials/specials_controller.py @@ -214,18 +214,18 @@ class SpecialsController: blob += "Martial Artist\n" blob += f"Speed: {ma_info.ma_speed:.2f} / {ma_info.ma_speed:.2f} secs\n" - blob += f"Damage: {ma_info.ma_min_dmg:d} - {ma_info.ma_max_dmg:d} " \ - f"({ma_info.ma_crit_dmg:d})\n\n" + blob += f"Damage: {ma_info.ma_min_dmg:.2f} - {ma_info.ma_max_dmg:.2f} " \ + f"({ma_info.ma_crit_dmg:.2f})\n\n" blob += "Shade\n" blob += f"Speed: {ma_info.shade_speed:.2f} / {ma_info.shade_speed:.2f} secs\n" - blob += f"Damage: {ma_info.shade_min_dmg:d} - {ma_info.shade_max_dmg:d} " \ - f"({ma_info.shade_crit_dmg:d})\n\n" + blob += f"Damage: {ma_info.shade_min_dmg:.2f} - {ma_info.shade_max_dmg:.2f} " \ + f"({ma_info.shade_crit_dmg:.2f})\n\n" blob += "All other professions\n" blob += f"Speed: {ma_info.gen_speed:.2f} / {ma_info.gen_speed:.2f} secs\n" - blob += f"Damage: {ma_info.gen_min_dmg:d} - {ma_info.gen_max_dmg:d} " \ - f"({ma_info.gen_crit_dmg:d})\n\n" + blob += f"Damage: {ma_info.gen_min_dmg:.2f} - {ma_info.gen_max_dmg:.2f} " \ + f"({ma_info.gen_crit_dmg:.2f})\n\n" return ChatBlob("Martial Arts Results", blob) diff --git a/modules/standard/whois/character_info_controller.py b/modules/standard/whois/character_info_controller.py index 25e0eee..264e461 100644 --- a/modules/standard/whois/character_info_controller.py +++ b/modules/standard/whois/character_info_controller.py @@ -108,12 +108,12 @@ class CharacterInfoController: blob += self.alts_controller.format_alt_list(alts) more_info = self.text.paginate_single(ChatBlob("More Info", blob)) - - msg = self.text.format_char_info(char_info, online_status, True) + " " + more_info + msg = ChatBlob("More Info", blob, self.text.format_char_info(char_info, online_status, True)+" ") + # msg = self.text.format_char_info(char_info, online_status, True) + " " + more_info elif char.char_id: blob = "Note: Could not retrieve detailed info for character.\n\n" blob += f"Name: {char.name}\n" - blob += f"Character ID: {char.char_id:d}\n" + blob += f"Character ID: {char.char_id}\n" if online_status is not None: blob += f"Online status: {'Online' if online_status else 'Offline'}\n" blob += self.get_name_history(char.char_id) diff --git a/requirements.txt b/requirements.txt index 401bb23..3ed08c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ bbcode==1.1.0 beautifulsoup4==4.10.0 cryptography==3.3.2 -discord.py @ git+https://github.com/Rapptz/discord.py@45d498c1b76deaf3b394d17ccf56112fa691d160 +discord.py @ git+https://github.com/Rapptz/discord.py@1bfe6b2bb160ce802a1f08afed73941a19a0a651 emojis==0.6.0 hjson==3.0.2 mariadb==1.0.7