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.
This commit is contained in:
2022-04-15 17:05:30 +02:00
parent fd84d82af2
commit a3a26f2ba4
32 changed files with 1036 additions and 729 deletions
+2
View File
@@ -13,6 +13,8 @@ from core.logger import Logger
from core.registry import Registry from core.registry import Registry
from core.upgrade import run_upgrades from core.upgrade import run_upgrades
import faulthandler
faulthandler.enable()
def get_config_from_env(): def get_config_from_env():
config_obj = DictObject() config_obj = DictObject()
+2 -1
View File
@@ -1,9 +1,10 @@
class ChatBlob: class ChatBlob:
def __init__(self, title, msg, prefix="", suffix=""): def __init__(self, title, msg, prefix="", suffix="", embed=True):
self.title = title self.title = title
self.msg = msg.strip("\n") self.msg = msg.strip("\n")
self.page_prefix = prefix self.page_prefix = prefix
self.page_postfix = suffix self.page_postfix = suffix
self.embed = embed
def __str__(self): def __str__(self):
return f"ChatBlob('{self.title}', '{self.msg}')" return f"ChatBlob('{self.title}', '{self.msg}')"
+26 -16
View File
@@ -66,6 +66,10 @@ class CommandService:
self.register_command_channel("Private Message", self.PRIVATE_MESSAGE_CHANNEL) self.register_command_channel("Private Message", self.PRIVATE_MESSAGE_CHANNEL)
self.register_command_channel("Org Channel", self.ORG_CHANNEL) self.register_command_channel("Org Channel", self.ORG_CHANNEL)
self.register_command_channel("Private Channel", self.PRIVATE_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): def start(self):
access_levels = {} access_levels = {}
@@ -205,16 +209,10 @@ class CommandService:
if not followup: if not followup:
if channel == "msg": if channel == "msg":
if message.lower().startswith("mail"): if message.lower().startswith("mail"):
self.relay_hub_service.send_message("tell_logger", char_id, "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|",
"mail |usage hidden|") f"[FROM] {self.character_service.resolve_char_to_name(char_id)}: mail |usage hidden|")
else: else:
self.relay_hub_service.send_message("tell_logger", 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}")
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)
# message = html.unescape(message) # message = html.unescape(message)
command_str, command_args = self.get_command_parts(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, self.relay_hub_service.send_message("access_denied_logger", sender,
f"[ERROR] {sender.name}: {message}", f"[ERROR] {sender.name}: {message}",
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 # record command usage
self.usage_service.add_usage(command_str, handler["callback"].__qualname__, char_id, channel) self.usage_service.add_usage(command_str, handler["callback"].__qualname__, char_id, channel)
else: else:
self.access_denied_response(message, sender, cmd_config, reply) self.access_denied_response(message, sender, cmd_config, reply, channel)
else: else:
# handlers were found, but no handler regex matched # handlers were found, but no handler regex matched
help_text = self.get_help_text(char_id, command_str, channel) 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)) reply(self.format_help_text(command_str, help_text))
else: else:
# the command is known, but no help is returned, therefore user does not have access to command # 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": # if access_level['label'] != "all":
# self.relay_hub_service.send_message("access_denied_logger", sender, # self.relay_hub_service.send_message("access_denied_logger", sender,
# f"[DENIED] {sender.name}: {message}", # f"[DENIED] {sender.name}: {message}",
@@ -280,20 +281,29 @@ class CommandService:
0) 0)
self.relay_hub_service.send_message("access_denied_logger", sender, f"[ERROR] {sender.name}: {message}", self.relay_hub_service.send_message("access_denied_logger", sender, f"[ERROR] {sender.name}: {message}",
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): def handle_unknown_command(self, command_str, command_args, channel, sender, reply):
self.relay_hub_service.send_message("access_denied_logger", sender, 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}",
f"[UNKNOWN] {sender.name}: {command_str} {command_args}") f"[UNKNOWN] {sender.name}: {command_str} {command_args}")
if sender.access_level["label"] != "all": 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}", self.relay_hub_service.send_message("access_denied_logger", sender, f"[DENIED] {sender.name}: {message}",
f"[DENIED] {sender.name}: {message}") f"[DENIED] {sender.name}: {message}")
if sender.access_level["label"] != "all": 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): def get_command_parts(self, message):
parts = message.split(" ", 1) parts = message.split(" ", 1)
+2 -2
View File
@@ -53,8 +53,8 @@ class Conn(Bot):
if num_messages > 30: if num_messages > 30:
self.logger.warning("automatically clearing outgoing message queue (%d messages)" % num_messages) self.logger.warning("automatically clearing outgoing message queue (%d messages)" % num_messages)
self.packet_queue.clear() self.packet_queue.clear()
elif num_messages > 10: # elif num_messages > 10:
self.logger.warning("%d messages in outgoing message queue" % num_messages) # self.logger.warning("%d messages in outgoing message queue" % num_messages)
def __str__(self): def __str__(self):
return self.id return self.id
+17 -9
View File
@@ -1,6 +1,7 @@
import inspect import inspect
import threading import threading
import time import time
import typing
from conf.config import BotConfig from conf.config import BotConfig
from core.aochat import server_packets, client_packets from core.aochat import server_packets, client_packets
@@ -11,15 +12,19 @@ from core.conn import Conn
from core.db import DB from core.db import DB
from core.decorators import instance from core.decorators import instance
from core.dict_object import DictObject from core.dict_object import DictObject
from core.fifo_queue import FifoQueue from core.fifo_queue import FifoQueue
from core.job_scheduler import JobScheduler from core.job_scheduler import JobScheduler
from core.logger import Logger 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 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") @instance("bot")
@@ -42,8 +47,8 @@ class IgnCore:
self.dimension = None self.dimension = None
self.last_timer_event = 0 self.last_timer_event = 0
self.start_time = int(time.time()) self.start_time = int(time.time())
self.major_version = "IGNCore v2.8" self.major_version = "IGNCore v2.9"
self.minor_version = "4" self.minor_version = "0"
self.incoming_queue = FifoQueue() self.incoming_queue = FifoQueue()
self.mass_message_queue = None self.mass_message_queue = None
self.conns = DictObject() self.conns = DictObject()
@@ -55,7 +60,7 @@ class IgnCore:
self.text: Text = registry.get_instance("text") self.text: Text = registry.get_instance("text")
self.setting_service: SettingService = registry.get_instance("setting_service") self.setting_service: SettingService = registry.get_instance("setting_service")
self.access_service: AccessService = registry.get_instance("access_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.job_scheduler: JobScheduler = registry.get_instance("job_scheduler")
self.command_service = registry.get_instance("command_service") self.command_service = registry.get_instance("command_service")
@@ -113,7 +118,7 @@ class IgnCore:
"created_at INT NOT NULL, " "created_at INT NOT NULL, "
"INDEX `command` (`command`) USING BTREE, " "INDEX `command` (`command`) USING BTREE, "
"INDEX `char_id` (`char_id`) 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 = 0")
self.db.exec("UPDATE db_version SET verified = 1 WHERE file = 'db_version'") self.db.exec("UPDATE db_version SET verified = 1 WHERE file = 'db_version'")
@@ -440,5 +445,8 @@ class IgnCore:
def get_char_name(self): def get_char_name(self):
return self.conns["main"].char_name return self.conns["main"].char_name
def get_conn(self):
return self.conns["main"]
def get_char_id(self): def get_char_id(self):
return self.conns["main"].char_id return self.conns["main"].char_id
+2
View File
@@ -32,6 +32,8 @@ class Logger:
def format_chat_message(self, msg): def format_chat_message(self, msg):
msg = re.sub(r"<a\s+href=\".+?[^\\]\">", "[link]", msg, flags=re.UNICODE | re.DOTALL) msg = re.sub(r"<a\s+href=\".+?[^\\]\">", "[link]", msg, flags=re.UNICODE | re.DOTALL)
msg = re.sub(r"<a\s+href='.+?'>", "[link]", msg, flags=re.UNICODE | re.DOTALL) msg = re.sub(r"<a\s+href='.+?'>", "[link]", msg, flags=re.UNICODE | re.DOTALL)
msg = re.sub(r"<a\s+href=user://\w+?>(\w+?)</a>", "\1", msg, flags=re.UNICODE | re.DOTALL)
msg = re.sub(r"<font\s+.+?>", "", msg, flags=re.UNICODE) msg = re.sub(r"<font\s+.+?>", "", msg, flags=re.UNICODE)
msg = re.sub("</font>", "", msg, flags=re.UNICODE) msg = re.sub("</font>", "", msg, flags=re.UNICODE)
msg = re.sub("</a>", "[/link]", msg, flags=re.UNICODE) msg = re.sub("</a>", "[/link]", msg, flags=re.UNICODE)
+3 -1
View File
@@ -50,7 +50,9 @@ class MessageHubService:
f"Incorrect number of arguments for handler '{callback.__module__}.{callback.__name__}()'") f"Incorrect number of arguments for handler '{callback.__module__}.{callback.__name__}()'")
if destination in self.hub: 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: for source in default_sources:
if source not in self.sources: if source not in self.sources:
+1
View File
@@ -39,6 +39,7 @@ class SettingService:
if module.split(".")[0] not in self.bot.modules: if module.split(".")[0] not in self.bot.modules:
return return
setting.set_name(name) setting.set_name(name)
setting.module = module
setting.set_description(description) setting.set_description(description)
setting.set_extended_description(extended_description) setting.set_extended_description(extended_description)
+1
View File
@@ -9,6 +9,7 @@ class SettingType:
def __init__(self): def __init__(self):
self.setting_service = Registry.get_instance("setting_service") self.setting_service = Registry.get_instance("setting_service")
self.name = None self.name = None
self.module = ""
def set_name(self, name): def set_name(self, name):
self.name = name self.name = name
@@ -274,4 +274,4 @@ class AccountController:
rows = self.account_service.get_logs(alts[0].char_id, limit=20) rows = self.account_service.get_logs(alts[0].char_id, limit=20)
for i in rows: for i in rows:
response += self.account_service.format_entry(i) 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)
@@ -1,3 +1,5 @@
import typing
from core.buddy_service import BuddyService from core.buddy_service import BuddyService
from core.chat_blob import ChatBlob from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService from core.command_alias_service import CommandAliasService
@@ -11,8 +13,9 @@ from core.text import Text
from core.translation_service import TranslationService from core.translation_service import TranslationService
from core.igncore import IgnCore from core.igncore import IgnCore
from core.util import Util from core.util import Util
from modules.core.accounting.services.account_service import AccountService if typing.TYPE_CHECKING:
from modules.core.discord.discord_controller import DiscordController from modules.core.accounting.services.account_service import AccountService
from modules.core.discord.discord_controller import DiscordController
@instance() @instance()
+6
View File
@@ -135,6 +135,12 @@
"en_US": "Could not find setting <highlight>{setting}</highlight>.", "en_US": "Could not find setting <highlight>{setting}</highlight>.",
"de_DE": "Die Einstellung <highlight>{setting}</highlight> wurde nicht gefunden." "de_DE": "Die Einstellung <highlight>{setting}</highlight> wurde nicht gefunden."
}, },
"setting_owner": {
"en_US": "Setting owned by module: <highlight>{value}</highlight>\n",
"de_DE": "Diese Einstellung gehört zu dem Modul: <highlight>{value}</highlight>\n"
},
"current_value": { "current_value": {
"en_US": "Current Value: <highlight>{value}</highlight>\n", "en_US": "Current Value: <highlight>{value}</highlight>\n",
"de_DE": "Aktueller Wert: <highlight>{value}</highlight>\n" "de_DE": "Aktueller Wert: <highlight>{value}</highlight>\n"
+1
View File
@@ -160,6 +160,7 @@ class ConfigController:
setting = self.setting_service.get(setting_name) setting = self.setting_service.get(setting_name)
if setting: 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", "current_value", {"value": str(setting.get_display_value())})
blob += self.getresp("module/config", "description", {"desc": setting.get_description()}) blob += self.getresp("module/config", "description", {"desc": setting.get_description()})
if setting.get_extended_description(): if setting.get_extended_description():
@@ -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"<img src='tdb://id:GFX_GUI_ICON_PROFESSION_(\d+)'>", ctx)
for match in m:
ctx = ctx.replace(f"<img src='tdb://id:GFX_GUI_ICON_PROFESSION_{match}'>",
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"(<br>|\n|</br>)", r"\n> "),
(r"<highlight>(.*?)</highlight>", r"**\1**"),
(r"<title>(.*?)</title>", r"**__\1__**"),
(r"<header>(.*?)</header>", r"**__\1__**"),
(r"<header2>(.*?)</header2>", r"__\1__"),
(r"<notice>(.*?)</notice>", r"*\1*"),
(r"<font color=('*?).+?\1>(.*?)</font>", r'\2'),
(r"<myname>", self.bot.get_char_name()),
(r"<symbol>", self.discord.setting_service.get_value("symbol")),
(r"<a href=(.*?)itemref://\d+/\d+/\d+\1>(.+?)<\/a>", r"`\2`"),
(r"<(.+?)>\s*?<\1>", ''),
(r"<pagebreak>", ''),
(r"<a.+?href=\'.*?\'>(.*?)</a>", r'`\1`'),
(r"<clan>(.*?)</clan>", r":yellow_circle: \1"),
(r"<omni>(.*?)</omni>", r":blue_circle: \1"),
(r"<neutral>(.*?)</neutral>", r":white_circle: \1"),
(r"<tab>", "\t"),
(r"<img src=(.?)rdb://\d+\1>", ''),
('#', ''),
('&lt;', '<'),
('&gt;', '>')]:
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+?)</\1>", 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)
+86 -388
View File
@@ -1,36 +1,38 @@
import asyncio import asyncio
import html
import logging import logging
import re import re
import threading
import time import time
from asyncio import BaseEventLoop from datetime import datetime, timedelta
from functools import partial
from html.parser import HTMLParser from html.parser import HTMLParser
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
import discord from typing import TYPE_CHECKING
import emojis as emojis
# noinspection PyPackageRequirements
from discord import Message, TextChannel, Guild, Embed, Role
# noinspection PyPackageRequirements
import discord
from discord import Message, TextChannel, Embed, Role, Guild
from core.chat_blob import ChatBlob from core.chat_blob import ChatBlob
from core.command_param_types import Const
from core.db import DB 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.dict_object import DictObject
from core.logger import Logger from core.logger import Logger
from core.lookup.character_service import CharacterService from core.setting_types import HiddenSettingType, NumberSettingType, TextSettingType, BooleanSettingType
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.text import Text from core.text import Text
from core.igncore import IgnCore from core.igncore import IgnCore
from core.util import Util from core.util import Util
from modules.core.accounting.services.access_service import AccessService from modules.core.discord.discord_data import DiscordData
from modules.core.accounting.services.account_service import AccountService from modules.core.discord.discord_management import DiscordManager
from modules.core.ban.ban_service import BanService
from modules.onlinebot.online.org_alias_controller import OrgAliasController 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): class MLStripper(HTMLParser):
@@ -61,18 +63,11 @@ class DiscordController:
logging.getLogger("discord.gateway").setLevel(logging.WARN) logging.getLogger("discord.gateway").setLevel(logging.WARN)
logging.getLogger("discord.client").setLevel(logging.WARN) logging.getLogger("discord.client").setLevel(logging.WARN)
intents = discord.Intents.all() intents = discord.Intents.all()
intents.message_content = True
self.client = discord.Client(intents=intents, chunk_guilds_at_startup=True) self.client = discord.Client(intents=intents, chunk_guilds_at_startup=True)
self.client.event(self.on_ready) self.manager: DiscordManager = DiscordManager(self)
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.client.event(self.on_message) self.client.event(self.on_message)
self.guild = None self.guild: Guild = None
self.channel = None self.channel = None
self.thread = None self.thread = None
self.invites = None self.invites = None
@@ -106,9 +101,17 @@ class DiscordController:
self.account_service: AccountService = registry.get_instance("account_service") self.account_service: AccountService = registry.get_instance("account_service")
self.setting_service: SettingService = registry.get_instance("setting_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): def pre_start(self):
self.setting_service.register(self.module_name, "discord_token", "", HiddenSettingType(allow_empty=True), self.setting_service.register(self.module_name, "discord_token", "", HiddenSettingType(allow_empty=True),
"Enter your Discord token here") "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): def get_name(self, discord_id):
data = self.db.query_single( data = self.db.query_single(
@@ -118,242 +121,9 @@ class DiscordController:
def setting_discord_token(self): def setting_discord_token(self):
return self.setting_service.get("discord_token") 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): def run_it_forever(self):
self.loop.run_forever() 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): def discord_update_account(self, char_id):
account = self.account_service.get_account(char_id) account = self.account_service.get_account(char_id)
if account is None: if account is None:
@@ -371,65 +141,12 @@ class DiscordController:
return "Processing..." return "Processing..."
return "No Discord account found" 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: def get_role(self, name, roles) -> Role or None:
for role in roles: for role in roles:
if role.name == name: if role.name == name:
return role return role
return None 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): async def discord_update_bot_basic(self):
self.guild = self.client.get_guild(self.client.guilds[0].id) self.guild = self.client.get_guild(self.client.guilds[0].id)
self.channel = self.client.get_channel(self.guild.text_channels[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) await self.client.start(token)
except discord.ClientException as exc: 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=""): async def discord_create_invite(self, reason=""):
created_invite = await self.channel.create_invite(max_age=300, max_uses=2, reason=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) await discord_user.remove_roles(role)
async def on_message(self, msg: Message): 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: if msg.author.id == self.client.user.id:
return return
if not self.setting_service.get_value("dc_relay_public"):
return
channel: TextChannel = msg.channel channel: TextChannel = msg.channel
if channel.id == int(self.setting_service.get_value("dc_relay_public")): acc = self.data.get_account_discord_id(msg.author.id)
if acc:
if self.setting_service.get_value('is_alliance_bot') == "0": acc.discord_nick = msg.author.nick or msg.author.name+'#'+msg.author.discriminator
response = f"[<notice>{html.escape(msg.author.nick if msg.author.nick else msg.author.name, False)}" \ prefix = f"[{self.alias_controller.get_alias(acc.member)}] {acc.name}: " if self.alias_usage().get_value() else f"[DC] {acc.name}: "
f"</notice>]: " \ self.relay_hub_service.send_message("Discord_(" + channel.name+")",
f"{html.escape(emojis.decode(msg.clean_content), False)}" DictObject(acc), msg.content,
else: prefix + msg.content)
response = f"{html.escape(msg.author.nick if msg.author.nick else msg.author.name, False)}: " \ else:
f"{html.escape(emojis.decode(msg.clean_content), False)}" prefix = f"[-UNK-] {msg.author.username}: " if self.alias_usage().get_value() else f"[DC] {msg.author.username}: "
await msg.delete(delay=3600) self.relay_hub_service.send_message("Discord_(" + channel.name+")",
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]) DictObject({'char_id': 0, 'name': msg.author.username, 'discord_handle': f'{msg.author.name}#{msg.author.discriminator}'}), msg.content,
self.relay_hub_service.send_message("public_relay", sender, html.escape(emojis.decode(msg.clean_content), False), response) 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): if self.is_command(msg.content):
admin = self.get_role("Administrator", self.guild.roles) admin = self.get_role("Administrator", self.guild.roles)
council = self.get_role("Council", self.guild.roles) council = self.get_role("Council", self.guild.roles)
if msg.content[:4] == "!pin": if msg.content[:4] == "!pin":
if admin in msg.author.roles: if admin in msg.author.roles:
matches = re.findall(pattern="<head>(.+?)<\/head>(.+)", string=msg.content[4:].strip(), matches = re.findall(pattern="<head>(.+?)<\/head>(.+)", string=msg.content[4:].strip(),
@@ -611,7 +359,7 @@ class DiscordController:
mess = await channel.send(embed=Embed(color=3066993, description=msg.content[4:])) mess = await channel.send(embed=Embed(color=3066993, description=msg.content[4:]))
await mess.pin() await mess.pin()
if msg.content[:5] == "!note": if msg.content[:6] == "!note ":
if admin in msg.author.roles: if admin in msg.author.roles:
matches = re.findall(pattern="<head>(.+?)<\/head>(.+)", string=msg.content[5:].strip(), matches = re.findall(pattern="<head>(.+?)<\/head>(.+)", string=msg.content[5:].strip(),
flags=re.DOTALL) flags=re.DOTALL)
@@ -636,73 +384,23 @@ class DiscordController:
else: else:
await msg.channel.purge(check=self.check, limit=count) await msg.channel.purge(check=self.check, limit=count)
await msg.delete(delay=0) 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): def is_command(self, message: str):
for x in ["subscribe", "pin", "purge", "note"]: for x in ["subscribe", "pin", "purge", "note"]:
if message.startswith("!" + x): if message.startswith("!" + x):
return True 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): 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 return False
else: else:
return True return True
@event(event_type="main_changed", description="Fix discord names & ranks") def alias_usage(self) -> BooleanSettingType:
def fix_ranks(self, _, data): return self.setting_service.get("is_alliance_bot")
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_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 public_relay(self) -> TextSettingType:
def change_count(self, _, _1): return self.setting_service.get("public_relay")
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)
+25
View File
@@ -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])
@@ -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)
@@ -235,22 +235,25 @@ class PrivateChannelController:
@event(event_type=IgnCore.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT, @event(event_type=IgnCore.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT,
description="Relay commands from the private channel to the relay hub", is_hidden=True) description="Relay commands from the private channel to the relay hub", is_hidden=True)
def outgoing_private_channel_message_event(self, _, event_data): def outgoing_private_channel_message_event(self, _, event_data):
if isinstance(event_data.message, ChatBlob): if isinstance(event_data.message, ChatBlob):
pages = self.text.paginate(event_data.message, pages = self.text.paginate(event_data.message,
self.setting_service.get("org_channel_max_page_length").get_value()) self.setting_service.get("org_channel_max_page_length").get_value())
if len(pages) < 4: if len(pages) < 4:
for page in pages: 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, self.message_hub_service.send_message(self.MESSAGE_SOURCE,
None, None,
page, page,
message) event_data.message)
else: 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, self.message_hub_service.send_message(self.MESSAGE_SOURCE,
None, None,
event_data.message.title, event_data.message.title,
message) event_data.message)
else: else:
message = "{priv}{message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message) message = "{priv}{message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message)
self.message_hub_service.send_message(self.MESSAGE_SOURCE, self.message_hub_service.send_message(self.MESSAGE_SOURCE,
+12 -3
View File
@@ -1,20 +1,26 @@
import json import json
import math import math
import typing
from core.buddy_service import BuddyService from core.buddy_service import BuddyService
from core.chat_blob import ChatBlob from core.chat_blob import ChatBlob
from core.command_param_types import Const, Character, Any, NamedParameters from core.command_param_types import Const, Character, Any, NamedParameters
from core.decorators import instance, command, event from core.decorators import instance, command, event
from core.dict_object import DictObject from core.dict_object import DictObject
from core.logger import Logger 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_service import SettingService
from core.setting_types import TextSettingType from core.setting_types import TextSettingType
from core.text import Text from core.text import Text
from core.translation_service import TranslationService from core.translation_service import TranslationService
from core.igncore import IgnCore 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 # noinspection SqlCaseVsIf,SqlCaseVsCoalesce
@@ -38,6 +44,7 @@ class RIAdminController:
self.getresp = self.ts.get_response self.getresp = self.ts.get_response
self.buddy_service: BuddyService = registry.get_instance("buddy_service") self.buddy_service: BuddyService = registry.get_instance("buddy_service")
self.pork: PorkService = registry.get_instance("pork_service") self.pork: PorkService = registry.get_instance("pork_service")
self.event_service: EventService = registry.get_instance("event_service")
def start(self): def start(self):
self.db.exec( self.db.exec(
@@ -54,6 +61,8 @@ class RIAdminController:
TextSettingType(), "Allowed bots (charID's)", TextSettingType(), "Allowed bots (charID's)",
extended_description="This setting is *NOT* synchronized across the network;" extended_description="This setting is *NOT* synchronized across the network;"
" this needs to be done manually!") " 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") @event(event_type="buddy_logoff", description="Track raiders")
def raider_logoff(self, _, event_data): def raider_logoff(self, _, event_data):
@@ -1,112 +1,112 @@
from core.aochat import server_packets, client_packets # from core.aochat import server_packets, client_packets
from core.conn import Conn # from core.conn import Conn
from core.decorators import instance # from core.decorators import instance
from core.igncore import IgnCore # from core.igncore import IgnCore
from core.logger import Logger # from core.logger import Logger
from core.lookup.character_service import CharacterService # from core.lookup.character_service import CharacterService
from core.setting_service import SettingService # from core.setting_service import SettingService
from core.setting_types import TextSettingType, BooleanSettingType, ColorSettingType # from core.setting_types import TextSettingType, BooleanSettingType, ColorSettingType
from core.text import Text # from core.text import Text
from modules.onlinebot.online.org_alias_controller import OrgAliasController # from modules.onlinebot.online.org_alias_controller import OrgAliasController
#
#
@instance("AllianceRelayController") # @instance("AllianceRelayController")
class AllianceRelayController: # class AllianceRelayController:
relay_channel_id = None # relay_channel_id = None
MESSAGE_SOURCE = "alliance" # MESSAGE_SOURCE = "alliance"
#
def __init__(self): # def __init__(self):
self.logger = Logger(__name__) # self.logger = Logger(__name__)
#
# noinspection DuplicatedCode # # noinspection DuplicatedCode
def inject(self, registry): # def inject(self, registry):
self.bot: IgnCore = registry.get_instance("bot") # self.bot: IgnCore = registry.get_instance("bot")
self.setting_service: SettingService = registry.get_instance("setting_service") # self.setting_service: SettingService = registry.get_instance("setting_service")
self.character_service: CharacterService = registry.get_instance("character_service") # self.character_service: CharacterService = registry.get_instance("character_service")
self.message_hub_service = registry.get_instance("message_hub_service") # self.message_hub_service = registry.get_instance("message_hub_service")
self.public_channel_service = registry.get_instance("public_channel_service") # self.public_channel_service = registry.get_instance("public_channel_service")
self.text: Text = registry.get_instance("text") # self.text: Text = registry.get_instance("text")
self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller") # self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller")
#
def pre_start(self): # def pre_start(self):
self.message_hub_service.register_message_source(self.MESSAGE_SOURCE) # self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)
#
def start(self): # def start(self):
self.setting_service.register(self.module_name, "arelaybot", "", # self.setting_service.register(self.module_name, "arelaybot", "",
TextSettingType(allow_empty=True), "Bot for alliance relay") # TextSettingType(allow_empty=True), "Bot for alliance relay")
#
self.setting_service.register(self.module_name, "arelay_enabled", False, # self.setting_service.register(self.module_name, "arelay_enabled", False,
BooleanSettingType(), "Enable the alliance relay") # BooleanSettingType(), "Enable the alliance relay")
#
self.setting_service.register(self.module_name, "arelay_color", "#C3C3C3", # self.setting_service.register(self.module_name, "arelay_color", "#C3C3C3",
ColorSettingType(), # ColorSettingType(),
"Color of messages from relay") # "Color of messages from relay")
#
self.message_hub_service.register_message_destination(self.MESSAGE_SOURCE, self.handle_relay_hub_message, [], # # self.message_hub_service.register_message_destination(self.MESSAGE_SOURCE, self.handle_relay_hub_message, [],
[self.MESSAGE_SOURCE]) # # [self.MESSAGE_SOURCE])
#
self.bot.register_packet_handler(server_packets.PrivateChannelInvited.id, self.handle_private_channel_invite, # self.bot.register_packet_handler(server_packets.PrivateChannelInvited.id, self.handle_private_channel_invite,
100) # 100)
self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message) # self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message)
#
# noinspection DuplicatedCode # # noinspection DuplicatedCode
def handle_private_channel_invite(self, conn: Conn, packet: server_packets.PrivateChannelInvited): # def handle_private_channel_invite(self, conn: Conn, packet: server_packets.PrivateChannelInvited):
if conn.id != "main": # if conn.id != "main":
return # return
#
if not self.setting_service.get("arelay_enabled").get_value(): # if not self.setting_service.get("arelay_enabled").get_value():
return # return
#
channel_name = self.character_service.get_char_name(packet.private_channel_id) # channel_name = self.character_service.get_char_name(packet.private_channel_id)
if self.setting_service.get_value("arelaybot").lower() == channel_name.lower(): # if self.setting_service.get_value("arelaybot").lower() == channel_name.lower():
self.bot.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id)) # self.bot.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id))
self.logger.info("Joined private channel {channel}".format(channel=channel_name)) # self.logger.info("Joined private channel {channel}".format(channel=channel_name))
self.relay_channel_id = packet.private_channel_id # self.relay_channel_id = packet.private_channel_id
#
# noinspection DuplicatedCode # # noinspection DuplicatedCode
def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage): # def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage):
if conn.id != "main": # if conn.id != "main":
return # return
#
if not self.setting_service.get("arelay_enabled").get_value(): # if not self.setting_service.get("arelay_enabled").get_value():
return # return
#
# ignore packets from the bot's own private channel and from the bot itself # # 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(): # if packet.private_channel_id == self.bot.get_char_id() or packet.char_id == self.bot.get_char_id():
return # return
#
message = packet.message.lstrip() # message = packet.message.lstrip()
if message[:6] != "!agcr ": # if message[:6] != "!agcr ":
return # return
#
message = message[6:] # message = message[6:]
formatted_message = self.setting_service.get("arelay_color").format_text(message) # formatted_message = self.setting_service.get("arelay_color").format_text(message)
#
sender = None # sender = None
self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, message, formatted_message) # self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, message, formatted_message)
#
def handle_relay_hub_message(self, ctx): # def handle_relay_hub_message(self, ctx):
if not self.setting_service.get("arelay_enabled").get_value(): # if not self.setting_service.get("arelay_enabled").get_value():
return # return
#
plain_msg = ctx.message or ctx.formatted_message # plain_msg = ctx.message or ctx.formatted_message
invite = self.text.make_chatcmd("click here", "/tell <myname> discord invite", # invite = self.text.make_chatcmd("click here", "/tell <myname> discord invite",
style="style='text-decoration:none'") # style="style='text-decoration:none'")
name = f"[{self.alias_controller.get_alias(ctx.sender.org_id)}] {ctx.sender.name}" # name = f"[{self.alias_controller.get_alias(ctx.sender.org_id)}] {ctx.sender.name}"
blob = self.text.format_page('Info', # blob = self.text.format_page('Info',
f"<header>::: Information :::</header><br><br>" # f"<header>::: Information :::</header><br><br>"
f"This message has been sent to you by:<br><br>" # f"This message has been sent to you by:<br><br>"
f"<header2>Igncom</header2><br>" # f"<header2>Igncom</header2><br>"
f"<notice>{ctx.sender.discord_handle}</notice><br>" # f"<notice>{ctx.sender.discord_handle}</notice><br>"
f"<highlight>{name}</highlight> on Alliance Discord.<br><br>" # f"<highlight>{name}</highlight> on Alliance Discord.<br><br>"
f"To reply, either respond in the relay or " # f"To reply, either respond in the relay or "
f"contact them directly at the provided handles.<br><br>" # f"contact them directly at the provided handles.<br><br>"
f"<header2>Have you joined The Alliance Discord yet? " # f"<header2>Have you joined The Alliance Discord yet? "
f"If not <highlight>{invite}</highlight> to receive an invite.</header2>") # f"If not <highlight>{invite}</highlight> to receive an invite.</header2>")
self.send_message_to_alliance(f"{name}: {plain_msg}" + f" <yellow>[{blob}]</yellow>") # self.send_message_to_alliance(f"{name}: {plain_msg}" + f" <yellow>[{blob}]</yellow>")
#
def send_message_to_alliance(self, msg): # def send_message_to_alliance(self, msg):
if self.relay_channel_id: # if self.relay_channel_id:
packet = client_packets.PrivateChannelMessage(self.relay_channel_id, # packet = client_packets.PrivateChannelMessage(self.relay_channel_id,
"!agcr " + self.text.format_message(msg, False), "\0") # "!agcr " + self.text.format_message(msg, False), "\0")
self.bot.conns["main"].send_packet(packet) # self.bot.conns["main"].send_packet(packet)
+6 -1
View File
@@ -3,6 +3,7 @@ import re
import time import time
from threading import Thread from threading import Thread
import datetime
import requests import requests
from core.buddy_service import BuddyService from core.buddy_service import BuddyService
@@ -359,4 +360,8 @@ class OrgController:
if len(current) > 10: if len(current) > 10:
out.append(current) out.append(current)
if len(out) > 0: 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)
+6 -4
View File
@@ -120,17 +120,19 @@ class OrgChannelController:
self.setting_service.get("org_channel_max_page_length").get_value()) self.setting_service.get("org_channel_max_page_length").get_value())
if len(pages) < 4: if len(pages) < 4:
for page in pages: 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, self.message_hub_service.send_message(self.MESSAGE_SOURCE,
None, None,
page, page,
message) event_data.message)
else: 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, self.message_hub_service.send_message(self.MESSAGE_SOURCE,
None, None,
event_data.message.title, event_data.message.title,
message) event_data.message)
else: else:
message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message) message = "{org} {message}".format(org=self.ORG_CHANNEL_PREFIX, message=event_data.message)
self.message_hub_service.send_message(self.MESSAGE_SOURCE, self.message_hub_service.send_message(self.MESSAGE_SOURCE,
@@ -76,9 +76,6 @@ class RaidbotController(BaseModule):
self.account_service: AccountService = registry.get_instance("account_service") self.account_service: AccountService = registry.get_instance("account_service")
self.buddy_service: BuddyService = registry.get_instance("buddy_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") @event("connect", "Adds all raiders to buddylist")
def connect(self, _, _1): def connect(self, _, _1):
query = self.db.query("SELECT char_id, member from account where member != -1 and disabled = 0") query = self.db.query("SELECT char_id, member from account where member != -1 and disabled = 0")
@@ -1,137 +1,137 @@
import asyncio # import asyncio
import datetime # import datetime
import html # import html
#
# noinspection PyPackageRequirements # # noinspection PyPackageRequirements
from discord import Embed # from discord import Embed
#
from core.command_service import CommandService # from core.command_service import CommandService
from core.db import DB # from core.db import DB
from core.decorators import instance # from core.decorators import instance
from core.logger import Logger # from core.logger import Logger
from core.lookup.character_service import CharacterService # from core.lookup.character_service import CharacterService
from core.lookup.pork_service import PorkService # from core.lookup.pork_service import PorkService
from core.message_hub_service import MessageHubService # from core.message_hub_service import MessageHubService
from core.registry import Registry # from core.registry import Registry
from core.setting_service import SettingService # from core.setting_service import SettingService
from core.setting_types import NumberSettingType # from core.setting_types import NumberSettingType
from core.text import Text # from core.text import Text
from core.igncore import IgnCore # from core.igncore import IgnCore
from modules.core.accounting.services.access_service import AccessService # from modules.core.accounting.services.access_service import AccessService
from modules.core.discord.discord_controller import DiscordController # from modules.core.discord.discord_controller import DiscordController
#
#
# noinspection DuplicatedCode,PyAttributeOutsideInit # # noinspection DuplicatedCode,PyAttributeOutsideInit
@instance() # @instance()
class MessageDistributor: # class MessageDistributor:
MESSAGE_SOURCE = "discord" # MESSAGE_SOURCE = "discord"
#
def __init__(self): # def __init__(self):
self.logger = Logger(__name__) # self.logger = Logger(__name__)
#
def inject(self, registry): # def inject(self, registry):
self.bot: IgnCore = registry.get_instance("bot") # self.bot: IgnCore = registry.get_instance("bot")
self.db: DB = registry.get_instance("db") # self.db: DB = registry.get_instance("db")
self.character_service: CharacterService = registry.get_instance("character_service") # self.character_service: CharacterService = registry.get_instance("character_service")
self.access_service: AccessService = registry.get_instance("access_service") # self.access_service: AccessService = registry.get_instance("access_service")
self.pork: PorkService = registry.get_instance("pork_service") # self.pork: PorkService = registry.get_instance("pork_service")
self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service") # self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service")
self.discord: DiscordController = registry.get_instance("discord_controller") # self.discord: DiscordController = registry.get_instance("discord_controller")
self.cmd_service: CommandService = registry.get_instance("command_service") # self.cmd_service: CommandService = registry.get_instance("command_service")
self.setting_service: SettingService = registry.get_instance("setting_service") # self.setting_service: SettingService = registry.get_instance("setting_service")
self.text: Text = registry.get_instance("text") # self.text: Text = registry.get_instance("text")
#
def pre_start(self): # def pre_start(self):
self.relay_hub_service.register_message_source("public_relay") # 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("tell_logger")
self.relay_hub_service.register_message_source("system_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("access_denied_logger")
self.relay_hub_service.register_message_source("member_logger") # self.relay_hub_service.register_message_source("member_logger")
#
def start(self): # def start(self):
self.relay_hub_service.register_message_destination("tell_log", self.handle_logging_tell, ["tell_logger"], # self.relay_hub_service.register_message_destination("tell_log", self.handle_logging_tell, ["tell_logger"],
["tell_log"]) # ["tell_log"])
self.setting_service.register(self.module_name, "dc_tell_log", 0, NumberSettingType(allow_empty=True), # self.setting_service.register(self.module_name, "dc_tell_log", 0, NumberSettingType(allow_empty=True),
"ChannelID for the Tell Log") # "ChannelID for the Tell Log")
#
self.relay_hub_service.register_message_destination("access_denied_log", self.handle_logging_denied, # self.relay_hub_service.register_message_destination("access_denied_log", self.handle_logging_denied,
["access_denied_logger"], ["access_denied_log"]) # ["access_denied_logger"], ["access_denied_log"])
self.setting_service.register(self.module_name, "dc_denied_log", 0, NumberSettingType(allow_empty=True), # self.setting_service.register(self.module_name, "dc_denied_log", 0, NumberSettingType(allow_empty=True),
"ChannelID for the Access Denied Log") # "ChannelID for the Access Denied Log")
#
self.relay_hub_service.register_message_destination("relay_log", self.handle_logging_relay, # self.relay_hub_service.register_message_destination("relay_log", self.handle_logging_relay,
["alliance", "public_relay"], ["relay_log"]) # ["alliance", "public_relay"], ["relay_log"])
self.setting_service.register(self.module_name, "dc_relay_log", 0, NumberSettingType(allow_empty=True), # self.setting_service.register(self.module_name, "dc_relay_log", 0, NumberSettingType(allow_empty=True),
"ChannelID for the Relay Log") # "ChannelID for the Relay Log")
#
self.relay_hub_service.register_message_destination("public_relay", self.handle_public_relay, ["alliance"], # self.relay_hub_service.register_message_destination("public_relay", self.handle_public_relay, ["alliance"],
["public_relay"]) # ["public_relay"])
self.setting_service.register(self.module_name, "dc_relay_public", 0, NumberSettingType(allow_empty=True), # self.setting_service.register(self.module_name, "dc_relay_public", 0, NumberSettingType(allow_empty=True),
"ChannelID for the Public Relay Channel") # "ChannelID for the Public Relay Channel")
#
self.relay_hub_service.register_message_destination("system", self.handle_logging_system, ["system_logger"], # self.relay_hub_service.register_message_destination("system", self.handle_logging_system, ["system_logger"],
["system"]) # ["system"])
self.setting_service.register(self.module_name, "dc_system_log", 0, NumberSettingType(allow_empty=True), # self.setting_service.register(self.module_name, "dc_system_log", 0, NumberSettingType(allow_empty=True),
"ChannelID for the System Channel") # "ChannelID for the System Channel")
#
self.relay_hub_service.register_message_destination("dc_member_log", self.handle_logging_members, # self.relay_hub_service.register_message_destination("dc_member_log", self.handle_logging_members,
["member_logger"], ["dc_member_log"]) # ["member_logger"], ["dc_member_log"])
self.setting_service.register(self.module_name, "dc_member_log", 0, NumberSettingType(allow_empty=True), # self.setting_service.register(self.module_name, "dc_member_log", 0, NumberSettingType(allow_empty=True),
"ChannelID for the Member Log") # "ChannelID for the Member Log")
#
if Registry.get_instance("darknet", is_optional=True): # if Registry.get_instance("darknet", is_optional=True):
self.relay_hub_service.register_message_destination("dc_darknet_log", self.handle_darknet_log, ["darknet"], # 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), # self.setting_service.register(self.module_name, "dc_darknet_log", 0, NumberSettingType(allow_empty=True),
"ChannelID for the Darknet Log") # "ChannelID for the Darknet Log")
#
def handle_public_relay(self, ctx): # def handle_public_relay(self, ctx):
if self.setting_service.get_value("dc_relay_public") != "0": # if self.setting_service.get_value("dc_relay_public") != "0":
message = self.format(ctx.formatted_message) # message = self.format(ctx.formatted_message)
self.discord.send_message(self.setting_service.get_value("dc_relay_public"), html.unescape(message), # self.discord.send_message(self.setting_service.get_value("dc_relay_public"), html.unescape(message),
delete=3600) # delete after 1h # delete=3600) # delete after 1h
#
def handle_logging_tell(self, ctx): # def handle_logging_tell(self, ctx):
if self.setting_service.get_value("dc_tell_log") != "0": # if self.setting_service.get_value("dc_tell_log") != "0":
message = self.format(ctx.message) # message = self.format(ctx.message)
self.discord.send_message(self.setting_service.get_value("dc_tell_log"), # self.discord.send_message(self.setting_service.get_value("dc_tell_log"),
f"[FROM] {self.character_service.get_char_name(ctx.sender)}: {message}") # f"[FROM] {self.character_service.get_char_name(ctx.sender)}: {message}")
#
def handle_logging_denied(self, ctx): # def handle_logging_denied(self, ctx):
if self.setting_service.get_value("dc_denied_log") != "0": # if self.setting_service.get_value("dc_denied_log") != "0":
message = self.format(ctx.message) # message = self.format(ctx.message)
self.discord.send_message(self.setting_service.get_value("dc_denied_log"), message) # self.discord.send_message(self.setting_service.get_value("dc_denied_log"), message)
#
def handle_darknet_log(self, ctx): # def handle_darknet_log(self, ctx):
if self.setting_service.get_value("dc_darknet_log") != "0": # if self.setting_service.get_value("dc_darknet_log") != "0":
message = self.format(ctx.message) # message = self.format(ctx.message)
self.discord.send_message(self.setting_service.get_value("dc_darknet_log"), message) # self.discord.send_message(self.setting_service.get_value("dc_darknet_log"), message)
#
def handle_logging_relay(self, ctx): # def handle_logging_relay(self, ctx):
if self.setting_service.get_value("dc_relay_log") != "0": # if self.setting_service.get_value("dc_relay_log") != "0":
message = self.format(ctx.formatted_message) # message = self.format(ctx.formatted_message)
self.discord.send_message(self.setting_service.get_value("dc_relay_log"), f"{html.unescape(message)}") # self.discord.send_message(self.setting_service.get_value("dc_relay_log"), f"{html.unescape(message)}")
#
def handle_logging_system(self, ctx): # def handle_logging_system(self, ctx):
if self.setting_service.get_value("dc_system_log") != "0": # if self.setting_service.get_value("dc_system_log") != "0":
message = self.format(ctx.message) # message = self.format(ctx.message)
self.discord.send_message(self.setting_service.get_value("dc_system_log"), f"{html.unescape(message)}") # self.discord.send_message(self.setting_service.get_value("dc_system_log"), f"{html.unescape(message)}")
#
def handle_logging_members(self, ctx): # def handle_logging_members(self, ctx):
if self.setting_service.get_value("dc_member_log") != "0": # 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"))) # channel = self.discord.client.get_channel(int(self.setting_service.get_value("dc_member_log")))
spam = [] # spam = []
current = 0 # current = 0
for message in ctx.message: # for message in ctx.message:
current += 1 # current += 1
spam.append(Embed(title=f"Recent changes - {datetime.date.today()} - ({current}/{len(ctx.message)})", # spam.append(Embed(title=f"Recent changes - {datetime.date.today()} - ({current}/{len(ctx.message)})",
description=message, color=3066993)) # description=message, color=3066993))
asyncio.run_coroutine_threadsafe(self.send_spam(channel, spam), self.discord.loop) # asyncio.run_coroutine_threadsafe(self.send_spam(channel, spam), self.discord.loop)
#
async def send_spam(self, channel, message): # async def send_spam(self, channel, message):
if self.discord.client.is_ready(): # if self.discord.client.is_ready():
for i in message: # for i in message:
await channel.send(embed=i) # await channel.send(embed=i)
#
def format(self, message): # def format(self, message):
return self.text.strip_html_tags(message) # return self.text.strip_html_tags(message)
@@ -1,4 +1,5 @@
import re import re
import typing
from core.aochat import server_packets, client_packets from core.aochat import server_packets, client_packets
from core.aochat.client_packets import PrivateChannelLeave from core.aochat.client_packets import PrivateChannelLeave
@@ -16,6 +17,10 @@ from core.igncore import IgnCore
# noinspection DuplicatedCode # noinspection DuplicatedCode
if typing.TYPE_CHECKING:
from modules.core.discord.discord_controller import DiscordController
@instance() @instance()
class AllianceRelay: class AllianceRelay:
MESSAGE_SOURCE = "alliance" MESSAGE_SOURCE = "alliance"
@@ -30,6 +35,7 @@ class AllianceRelay:
self.character_service: CharacterService = registry.get_instance("character_service") self.character_service: CharacterService = registry.get_instance("character_service")
self.message_hub_service = registry.get_instance("message_hub_service") self.message_hub_service = registry.get_instance("message_hub_service")
self.public_channel_service = registry.get_instance("public_channel_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") self.text: Text = registry.get_instance("text")
def pre_start(self): def pre_start(self):
@@ -54,17 +60,24 @@ class AllianceRelay:
if color: if color:
return f"<font color={color}>{msg}</font>" return f"<font color={color}>{msg}</font>"
return "UNSET" return "UNSET"
blob = "" blob = ""
for bot in self.relay_bots().get_value().keys(): 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')) 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')) 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)) base = self.relay_color_base().get_value().get(bot,
sender = self.relay_color_sender().get_value().get(bot, self.relay_color_sender().get_value().get('default', None)) 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)) 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)) 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')) our_abbrv = self.relay_guild_abbreviations().get_value().get(bot,
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")}') 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 <highlight>{self.character_service.get_char_name(int(bot))}</highlight>:\n" blob += f"Relay <highlight>{self.character_service.get_char_name(int(bot))}</highlight>:\n"
blob += f" Enabled: {enabled} [{self.text.make_tellcmd('Toggle', f'mrelay status {bot} {not enabled}')}]\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" 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 <highlight>{bot.name}</highlight>" return f"Successfully deleted relay <highlight>{bot.name}</highlight>"
@command(command="mrelay", @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") description="Change the relay command used in a relay")
def mrelay_rcmd(self, request, _, bot, relay_command): def mrelay_rcmd(self, request, _, bot, relay_command):
bots: dict = self.relay_bots().get_value() bots: dict = self.relay_bots().get_value()
@@ -173,7 +187,8 @@ class AllianceRelay:
f"<highlight>{self.character_service.get_char_name(int(bot))}</highlight>: <highlight>{relay_command}</highlight>" f"<highlight>{self.character_service.get_char_name(int(bot))}</highlight>: <highlight>{relay_command}</highlight>"
@command(command="mrelay", @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") description="Change the prefix setting of the relays")
def mrelay_prefix(self, request, _, bot, prefix): def mrelay_prefix(self, request, _, bot, prefix):
bots: dict = self.relay_bots().get_value() bots: dict = self.relay_bots().get_value()
@@ -234,7 +249,9 @@ class AllianceRelay:
return f"Successfully changed the status of the relay " \ return f"Successfully changed the status of the relay " \
f"<highlight>{self.character_service.get_char_name(int(bot))}</highlight> to: <highlight>{option}</highlight>" f"<highlight>{self.character_service.get_char_name(int(bot))}</highlight> to: <highlight>{option}</highlight>"
@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") description="Change the color settings of the relays")
def mrelay_color(self, request, _, bot, option, color): def mrelay_color(self, request, _, bot, option, color):
bots: dict = self.relay_bots().get_value() bots: dict = self.relay_bots().get_value()
@@ -312,7 +329,8 @@ class AllianceRelay:
return return
if str(packet.private_channel_id) not in self.relay_bots().get_value().keys(): if str(packet.private_channel_id) not in self.relay_bots().get_value().keys():
return 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 return
self.bot.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id)) 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)}") 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() text = text.strip()
plain = f"[{org}] {name}: {text}" 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) org = self.format_text(
name = self.format_text(self.relay_color_sender().get_value().get(priv, self.relay_color_sender().get_value().get("default")), name) self.relay_color_org().get_value().get(priv, self.relay_color_org().get_value().get("default")), org)
text = self.format_text(self.relay_color_msg().get_value().get(priv, self.relay_color_msg().get_value().get("default")), text) name = self.format_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}") 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)}) 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): 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: if not ctx.message:
return return
if not ctx.sender:
return
if type(ctx.message) == ChatBlob:
return
for key in self.relay_bots().get_value().keys(): for key in self.relay_bots().get_value().keys():
if key not in self.relay_channel: if key not in self.relay_channel:
continue continue
if not enabled(key): if not enabled(key):
continue continue
prefix = self.relay_symbols().get_value().get(key, self.relay_symbols().get_value().get('default', False)) 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 continue
sender = ctx.sender.name
message = ctx.message[len(prefix):].strip() message = ctx.message[len(prefix):].strip()
if len(message) < 1: if len(message) < 1:
continue continue
sender = ctx.sender.name
abbrv = self.relay_guild_abbreviations().get_value().get(key, self.setting_service.get_value("org_name")) abbrv = self.relay_guild_abbreviations().get_value().get(key, self.setting_service.get_value("org_name"))
if ctx.source == "private_channel": if ctx.source == "private_channel":
abbrv += " - Guest" abbrv += " - Guest"
elif ctx.source == "public_relay": elif ctx.source.startswith("Discord_("):
abbrv += " - DC" abbrv += " - DC"
cmd = self.relay_command().get_value().get(key, self.relay_command().get_value().get("default")) 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 <myname> discord invite",
style="style='text-decoration:none'")
# name = "<header2><myname></header2>"
if self.discord.guild and self.discord.bot:
name = f"<header2>DC Server: <highlight>{self.discord.guild.name}</highlight> - " \
f"Bot: <highlight><myname></highlight></header2>"
blob = self.text.format_page('Info',
f"<header>::: Information :::</header><br><br>"
f"This message has been sent to you by:<br><br>"
f"{name}<br>"
f"<tab>Sender: <notice>{ctx.sender.name}</notice><br>"
f"<tab>Tag: <highlight>{ctx.sender.discord_nick}</highlight><br><br>"
f"To reply, either respond in the relay or "
f"contact them directly at the provided handles.<br><br>"
f"<header2>Have you joined our Discord Server yet? "
f"If not <highlight>{invite}</highlight> to receive an invite.</header2>")
blob = f"<yellow>[{blob}]</yellow>"
# msg = f"{cmd} [{self.discord.alias_controller.get_alias(ctx.sender.org_id)}] {ctx.message} <yellow>[{blob}]</yellow>"
msg = f"{cmd} [{abbrv}] {sender}: {message} {blob}"
self.send_message_to_alliance(key, msg) self.send_message_to_alliance(key, msg)
def send_message_to_alliance(self, alliance, msg): def send_message_to_alliance(self, alliance, msg):
@@ -398,7 +450,8 @@ class AllianceRelay:
def relay_enabled(self) -> DictionarySettingType: def relay_enabled(self) -> DictionarySettingType:
return 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: def relay_guild_abbreviations(self) -> DictionarySettingType:
return DictionarySettingType() return DictionarySettingType()
@@ -431,3 +484,6 @@ class AllianceRelay:
def format_text(self, color, message): def format_text(self, color, message):
return f"<font color={color}>{message}</font>" return f"<font color={color}>{message}</font>"
# def alias_usage(self):
# return self.setting_service.get("use_prefix")
+1 -1
View File
@@ -47,7 +47,7 @@ class TimeController:
day += "rd" day += "rd"
else: else:
day += "th" 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." f"{calendar.month_name[now.month]} {day}, {now.year + 27474} Rubi-Ka Universal Time."
@command(command="time", params=[Any("timezone")], access_level="member", @command(command="time", params=[Any("timezone")], access_level="member",
+9 -7
View File
@@ -1,4 +1,5 @@
import time import time
import typing
from datetime import datetime from datetime import datetime
from core.buddy_service import BuddyService from core.buddy_service import BuddyService
@@ -8,16 +9,17 @@ from core.db import DB
from core.decorators import instance, command from core.decorators import instance, command
from core.job_scheduler import JobScheduler from core.job_scheduler import JobScheduler
from core.logger import Logger 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.text import Text
from core.igncore import IgnCore from core.igncore import IgnCore
from core.util import Util from core.util import Util
from modules.core.accounting.preference_controller import PreferenceController if typing.TYPE_CHECKING:
from modules.core.accounting.services.account_service import AccountService from core.lookup.pork_service import PorkService
from modules.core.ban.ban_service import BanService from core.setting_service import SettingService
from modules.core.discord.discord_controller import DiscordController from modules.core.accounting.preference_controller import PreferenceController
from modules.onlinebot.online.org_alias_controller import OrgAliasController 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() @instance()
+12 -3
View File
@@ -3,13 +3,14 @@ import time
from core.chat_blob import ChatBlob from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService from core.command_alias_service import CommandAliasService
from core.command_param_types import Any 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.dict_object import DictObject
from core.igncore import IgnCore from core.igncore import IgnCore
from core.job_scheduler import JobScheduler from core.job_scheduler import JobScheduler
from core.logger import Logger from core.logger import Logger
from core.message_hub_service import MessageHubService
from core.setting_service import SettingService 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.text import Text
from core.util import Util from core.util import Util
from modules.standard.datanet.ws_controller import WebsocketRelayController 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.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler") self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler")
self.setting_service: SettingService = registry.get_instance("setting_service") self.setting_service: SettingService = registry.get_instance("setting_service")
self.relay_hub: MessageHubService = registry.get_instance("message_hub_service")
def pre_start(self): def pre_start(self):
self.relay_hub.register_message_source("timers")
self.setting_service.register(self.module_name, 'timer_spam', False, BooleanSettingType(), self.setting_service.register(self.module_name, 'timer_spam', False, BooleanSettingType(),
"should timers be spammed") "should timers be spammed")
self.command_alias_service.add_alias("tara", "wb tara") self.command_alias_service.add_alias("tara", "wb tara")
@@ -120,7 +124,7 @@ class WorldBossController:
return DictObject({'spawn': last - immortal, 'mortal': last}) return DictObject({'spawn': last - immortal, 'mortal': last})
def send_warn(self, msg): def send_warn(self, msg):
self.bot.send_private_channel_message(f"[<red>WB</red>] {msg}") self.relay_hub.send_message("timers", None, f"[<red>WB</red>] {msg}", f"[<red>WB</red>] {msg}")
def get_next_alert(self, duration): def get_next_alert(self, duration):
for alert in self.alerts: for alert in self.alerts:
@@ -190,3 +194,8 @@ class WorldBossController:
job['id'] = job_id job['id'] = job_id
return return
self.jobs.append({'name': timer.name, 'id': job_id}) 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]'])
+3 -3
View File
@@ -39,11 +39,11 @@ class OnlineDisplay:
if org > 0: if org > 0:
postfix.append(f"<notice>Org: {org}</notice>") postfix.append(f"<notice>Org: {org}</notice>")
if priv > 0: if priv > 0:
postfix.append(f"<highlight>Priv: {priv}</notice>") postfix.append(f"<notice>Priv: {priv}</notice>")
if notify > 0: if notify > 0:
postfix.append(f"<notice>Buddylist: {notify}</notice>") postfix.append(f"<notice>Buddylist: {notify}</notice>")
blob = ChatBlob(title, blob, suffix=f" ({f', '.join(postfix)})") 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): def format_by_channel_main(self, query, params):
query += "order by channel_id, main_name, p.name=main_name, p.name, r_id" 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"]: if org_id != temp_blob[key]["id"]:
org_id = temp_blob[key]["id"] org_id = temp_blob[key]["id"]
blob += f"\n<pagebreak><highlight>{temp_blob[key]['name']}</highlight>" \ blob += f"\n<pagebreak><highlight>{temp_blob[key]['name']}</highlight>" \
f"<header> ({len(temp_blob[key]['online']): >3} " \ f" <header>({len(temp_blob[key]['online']): >3} " \
f"| {len(temp_blob[key]['online']) / len(query) * 100:.2f}%)</header>\n" f"| {len(temp_blob[key]['online']) / len(query) * 100:.2f}%)</header>\n"
blob += self.format_org(user) blob += self.format_org(user)
return blob, 0, 0, len(query) return blob, 0, 0, len(query)
@@ -214,18 +214,18 @@ class SpecialsController:
blob += "<header2>Martial Artist</header2>\n" blob += "<header2>Martial Artist</header2>\n"
blob += f"Speed: <highlight>{ma_info.ma_speed:.2f} / {ma_info.ma_speed:.2f} secs</highlight>\n" blob += f"Speed: <highlight>{ma_info.ma_speed:.2f} / {ma_info.ma_speed:.2f} secs</highlight>\n"
blob += f"Damage: <highlight>{ma_info.ma_min_dmg:d} - {ma_info.ma_max_dmg:d} " \ blob += f"Damage: <highlight>{ma_info.ma_min_dmg:.2f} - {ma_info.ma_max_dmg:.2f} " \
f"({ma_info.ma_crit_dmg:d})</highlight>\n\n" f"({ma_info.ma_crit_dmg:.2f})</highlight>\n\n"
blob += "<header2>Shade</header2>\n" blob += "<header2>Shade</header2>\n"
blob += f"Speed: <highlight>{ma_info.shade_speed:.2f} / {ma_info.shade_speed:.2f} secs</highlight>\n" blob += f"Speed: <highlight>{ma_info.shade_speed:.2f} / {ma_info.shade_speed:.2f} secs</highlight>\n"
blob += f"Damage: <highlight>{ma_info.shade_min_dmg:d} - {ma_info.shade_max_dmg:d} " \ blob += f"Damage: <highlight>{ma_info.shade_min_dmg:.2f} - {ma_info.shade_max_dmg:.2f} " \
f"({ma_info.shade_crit_dmg:d})</highlight>\n\n" f"({ma_info.shade_crit_dmg:.2f})</highlight>\n\n"
blob += "<header2>All other professions</header2>\n" blob += "<header2>All other professions</header2>\n"
blob += f"Speed: <highlight>{ma_info.gen_speed:.2f} / {ma_info.gen_speed:.2f} secs</highlight>\n" blob += f"Speed: <highlight>{ma_info.gen_speed:.2f} / {ma_info.gen_speed:.2f} secs</highlight>\n"
blob += f"Damage: <highlight>{ma_info.gen_min_dmg:d} - {ma_info.gen_max_dmg:d} " \ blob += f"Damage: <highlight>{ma_info.gen_min_dmg:.2f} - {ma_info.gen_max_dmg:.2f} " \
f"({ma_info.gen_crit_dmg:d})</highlight>\n\n" f"({ma_info.gen_crit_dmg:.2f})</highlight>\n\n"
return ChatBlob("Martial Arts Results", blob) return ChatBlob("Martial Arts Results", blob)
@@ -108,12 +108,12 @@ class CharacterInfoController:
blob += self.alts_controller.format_alt_list(alts) blob += self.alts_controller.format_alt_list(alts)
more_info = self.text.paginate_single(ChatBlob("More Info", blob)) more_info = self.text.paginate_single(ChatBlob("More Info", blob))
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 # msg = self.text.format_char_info(char_info, online_status, True) + " " + more_info
elif char.char_id: elif char.char_id:
blob = "<notice>Note: Could not retrieve detailed info for character.</notice>\n\n" blob = "<notice>Note: Could not retrieve detailed info for character.</notice>\n\n"
blob += f"Name: <highlight>{char.name}</highlight>\n" blob += f"Name: <highlight>{char.name}</highlight>\n"
blob += f"Character ID: <highlight>{char.char_id:d}</highlight>\n" blob += f"Character ID: <highlight>{char.char_id}</highlight>\n"
if online_status is not None: if online_status is not None:
blob += f"Online status: {'<green>Online</green>' if online_status else '<red>Offline</red>'}\n" blob += f"Online status: {'<green>Online</green>' if online_status else '<red>Offline</red>'}\n"
blob += self.get_name_history(char.char_id) blob += self.get_name_history(char.char_id)
+1 -1
View File
@@ -1,7 +1,7 @@
bbcode==1.1.0 bbcode==1.1.0
beautifulsoup4==4.10.0 beautifulsoup4==4.10.0
cryptography==3.3.2 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 emojis==0.6.0
hjson==3.0.2 hjson==3.0.2
mariadb==1.0.7 mariadb==1.0.7