Initial Release of IGNCore version 2.5

This commit is contained in:
2021-08-09 13:18:56 +02:00
commit a83d98c47e
910 changed files with 224171 additions and 0 deletions
@@ -0,0 +1,87 @@
from functools import partial
from core.chat_blob import ChatBlob
from core.command_param_types import Const, Any
from core.decorators import instance, command
@instance()
class MessageHubController:
def inject(self, registry):
self.bot = registry.get_instance("bot")
self.db = registry.get_instance("db")
self.text = registry.get_instance("text")
self.message_hub_service = registry.get_instance("message_hub_service")
self.getresp = partial(registry.get_instance("translation_service").get_response, "module/system")
def start(self):
pass
@command(command="messagehub", params=[], access_level="admin",
description="Show the current message hub subscriptions")
def messagehub_cmd(self, _):
blob = self.getresp("messagehub_info") + "\n"
subscriptions = self.message_hub_service.hub
for destination, obj in subscriptions.items():
edit_subs_link = self.text.make_tellcmd(destination, "messagehub edit %s" % destination)
blob += "\n%s\n" % edit_subs_link
for source in obj.sources:
blob += "%s\n" % source
return ChatBlob(self.getresp("messagehub_title", {"count": len(subscriptions)}), blob)
@command(command="messagehub", params=[Const("edit"), Any("destination")], access_level="admin",
description="Edit subscriptions for a destination")
def messagehub_edit_cmd(self, _, _1, destination):
obj = self.message_hub_service.hub[destination]
if not obj:
return self.getresp("destination_not_exist", {"destination": destination})
blob = ""
count = 0
for source in self.message_hub_service.sources:
if source in obj.invalid_sources:
continue
sub_link = self.text.make_tellcmd("Subscribe", "messagehub subscribe %s %s" % (destination, source))
unsub_link = self.text.make_tellcmd("Unsubscribe", "messagehub unsubscribe %s %s" % (destination, source))
status = ""
if source in obj.sources:
count += 1
status = "<green>%s</green>" % self.getresp("subscribed")
blob += "%s [%s] [%s] %s\n\n" % (source, sub_link, unsub_link, status)
return ChatBlob(
self.getresp("messagehub_edit_title", {"destination": destination.capitalize(), "count": count}), blob)
@command(command="messagehub",
params=[Const("subscribe"), Any("destination"), Any("source")],
access_level="admin",
description="Subscribe a destination to a source")
def messagehub_subscribe_cmd(self, _, _1, destination, source):
obj = self.message_hub_service.hub[destination]
if not obj:
return self.getresp("module/system", "destination_not_exist", {"destination": destination})
if source in obj.sources:
return self.getresp("messagehub_already_subscribed", {"destination": destination, "source": source})
if source in obj.invalid_sources:
return self.getresp("messagehub_invalid_subscription", {"destination": destination, "source": source})
self.message_hub_service.subscribe_to_source(destination, source)
return self.getresp("messagehub_subscribe_success", {"destination": destination, "source": source})
@command(command="messagehub", params=[Const("unsubscribe"), Any("destination"), Any("source")],
access_level="admin",
description="Unsubscribe a destination to a source")
def messagehub_unsubscribe_cmd(self, _, _1, destination, source):
obj = self.message_hub_service.hub[destination]
if not obj:
return self.getresp("module/system", "destination_not_exist", {"destination": destination})
if source not in obj.sources:
return self.getresp("messagehub_not_subscribed", {"destination": destination, "source": source})
self.message_hub_service.unsubscribe_from_source(destination, source)
return self.getresp("messagehub_unsubscribe_success", {"destination": destination, "source": source})
+20
View File
@@ -0,0 +1,20 @@
from core.command_param_types import Const
from core.decorators import instance, command
@instance()
class QueueController:
def inject(self, registry):
self.bot = registry.get_instance("bot")
self.command_alias_service = registry.get_instance("command_alias_service")
self.getresp = registry.get_instance("translation_service").get_response
def start(self):
self.command_alias_service.add_alias("clearqueue", "queue clear")
@command(command="queue", params=[Const("clear")], access_level="moderator",
description="Clear the outgoing message queue")
def queue_clear_cmd(self, _, _1):
num_messages = len(self.bot.conns["main"].packet_queue)
self.bot.conns["main"].packet_queue.clear()
return self.getresp("module/system", "clear_queue", {"count": num_messages})
+22
View File
@@ -0,0 +1,22 @@
from core.command_param_types import Any, Character
from core.decorators import instance, command
@instance()
class RunasController:
def inject(self, registry):
self.command_service = registry.get_instance("command_service")
self.access_service = registry.get_instance("access_service")
self.getresp = registry.get_instance("translation_service").get_response
@command(command="runas", params=[Character("character"), Any("command")], access_level="superadmin",
description="Run a command as another character")
def runas_cmd(self, request, char, command_str):
if not char.char_id:
return self.getresp("global", "char_not_found", {"char": char.name})
elif not self.access_service.has_sufficient_access_level(request.sender.char_id, char.char_id):
return self.getresp("module/system", "runas_fail", {"char": char.name})
else:
command_str = self.command_service.trim_command_symbol(command_str)
self.command_service.process_command(command_str, request.channel,
char.char_id, request.reply, request.conn)
@@ -0,0 +1,19 @@
from core.command_param_types import Any, Character
from core.decorators import instance, command
@instance()
class SendMessageController:
def inject(self, registry):
self.bot = registry.get_instance("bot")
self.command_service = registry.get_instance("command_service")
self.getresp = registry.get_instance("translation_service").get_response
@command(command="sendtell", params=[Character("character"), Any("message")], access_level="superadmin",
description="Send a tell to another character from the bot")
def sendtell_cmd(self, _, char, message):
if char.char_id:
self.bot.send_private_message(char.char_id, message, add_color=False)
return self.getresp("module/system", "msg_sent")
else:
return self.getresp("global", "char_not_found", {"char": char.name})
+57
View File
@@ -0,0 +1,57 @@
from core.decorators import instance
@instance()
class SqlController:
def inject(self, registry):
self.bot = registry.get_instance("bot")
self.db = registry.get_instance("db")
self.text = registry.get_instance("text")
self.command_alias_service = registry.get_instance("command_alias_service")
self.getresp = registry.get_instance("translation_service").get_response
# def start(self):
# self.command_alias_service.add_alias("querysql", "sql query")
# self.command_alias_service.add_alias("executesql", "sql exec")
# @command(command="sql", params=[Const("query"), Any("sql_statement")], access_level="superadmin",
# description="Execute a SQL query and return the results")
# def sql_query_cmd(self, request, _, sql):
# try:
# results = self.db.query(sql)
# return ChatBlob(self.getresp("module/system", "sql_blob_title", {"count": len(results)}),
# json.dumps(results, indent=4, sort_keys=True))
# except Exception as e:
# return self.getresp("module/system", "sql_fail", {"error": str(e)})
#
# @command(command="sql", params=[Const("exec"), Any("sql_statement")], access_level="superadmin",
# description="Execute a SQL query and return number of affected rows")
# def sql_exec_cmd(self, request, _, sql):
# try:
# row_count = self.db.exec(sql)
# return self.getresp("module/system", "sql_exec_success", {"count": row_count})
# except Exception as e:
# return self.getresp("module/system", "sql_fail", {"error": str(e)})
#
# @command(command="sql", params=[Const("files")], access_level="superadmin",
# description="Show SQL files that have been loaded")
# def sql_files_cmd(self, request, _):
# data = self.db.query("SELECT file, version FROM db_version ORDER BY file ASC")
#
# blob = ""
# for row in data:
# reload_link = ""
# if row.file != "db_version":
# reload_link = self.text.make_tellcmd("Reload", f"sql load {row.file}")
# blob += f"{row.file} - {row.version} {reload_link}\n"
#
# return ChatBlob("SQL Files (%d)" % len(data), blob)
#
# @command(command="sql", params=[Const("load"), Any("sql_file")], access_level="superadmin",
# description="Load or reload a SQL file")
# def sql_load_cmd(self, request, _, file):
# try:
# self.db.load_sql_file(file, force_update=True)
# return f"SQL file <highlight>{file}</highlight> has been loaded."
# except Exception as e:
# return self.getresp("module/system", "sql_fail", {"error": str(e)})
+164
View File
@@ -0,0 +1,164 @@
{
"reload_lang": {
"en_US": "Language changed to <highlight>{lang_code}</highlight>",
"de_DE": "Die Sprache wurde auf <highlight>{lang_code}</highlight> geändert."
},
"current_lang": {
"en_US": "My current language is <highlight>{lang_code}</highlight>",
"de_DE": "Meine aktuelle Sprache ist <highlight>{lang_code}</highlight>."
},
"clear_queue": {
"en_US": "Cleared <highlight>{count}</highlight> messages from the outgoing message queue.",
"de_DE": "Es wurden <highlight>{count}</highlight> Nachrichten aus der ausgehenden Nachrichten warteschlange entfernt."
},
"runas_fail": {
"en_US": "Error! You must have a higher access level than <highlight>{char}</highlight>.",
"de_DE": "Error! Du musst ein höheres Rechtelevel haben als <highlight>{char}</highlight>."
},
"msg_sent": {
"en_US": "Your message has been sent.",
"de_DE": "Deine Nachricht wurde erfolgreich gesendet."
},
"sql_exec_success": {
"en_US": "{count} row(s) affected.",
"de_DE": "{count} Zeile(n) betroffen."
},
"sql_fail": {
"en_US": "There was an error executing your query: {error}",
"de_DE": "Bei der Bearbeitung deiner Anfrage ist ein Fehler aufgetreten: {error}"
},
"sql_blob_title": {
"en_US": "Results ({count})",
"de_DE": "Ergebnisse ({count})"
},
"expected_online": {
"en_US": "<myname> is now <green>online</green>.",
"de_DE": "<myname> ist wieder <green>online</green>."
},
"unexpected_online": {
"en_US": "<myname> is now <green>online</green> but may have shut down or restarted unexpectedly.",
"de_DE": "<myname> ist wieder <green>online</green>, wurde aber möglicherweise unerwartet heruntergefahren, oder neugestartet."
},
"shutdown": {
"en_US": "The bot is shutting down.",
"de_DE": "Der Bot wird heruntergefahren."
},
"restart": {
"en_US": "The bot is restarting.",
"de_DE": "Der Bot startet nun neu."
},
"reason": {
"en_US": " <highlight>Reason: {reason}</highlight>",
"de_DE": " <highlight>Der Grund hierfür: {reason}</highlight>"
},
"check_access": {
"en_US": "Access level for <highlight>{char}</highlight> is <highlight>{rank_main}</highlight>.",
"de_DE": "Das Zugriffslevel für <highlight>{char}</highlight> ist <highlight>{rank_main}</highlight>."
},
"show_output_target": {
"en_US": "<highlight>{sender}</highlight> is showing you output for command <highlight>{cmd}</highlight>:",
"de_DE": "<highlight>{sender}</highlight> zeigt dir den output des Befehls <highlight>{cmd}</highlight>:"
},
"show_output_self": {
"en_US": "Command <highlight>{cmd}</highlight> output has been sent to <highlight>{target}</highlight>.",
"de_DE": "Die Ausgabe des Befehls <highlight>{cmd}</highlight> wurde an <highlight>{target}</highlight> weitergeleitet."
},
"status_blob": {
"en_US": [
"Version: <highlight>{bot_ver}</highlight>\n",
"Name: <highlight><myname></highlight>\n",
"\n",
"OS: <highlight>{os_ver}</highlight>\n",
"Python: <highlight>{python_ver}</highlight>\n",
"Database: <highlight>{db_type}</highlight>\n",
"Memory Usage: <highlight>{mem_usage} KB</highlight>\n",
"\n",
"Superadmin: <highlight>{superadmin}</highlight>\n",
"Buddy List: <highlight>{bl_used}/{bl_size}</highlight>\n",
"Uptime: <highlight>{uptime}</highlight>\n",
"Dimension: <highlight>{dim}</highlight>\n",
"\n",
"Org Id: <highlight>{org_id}</highlight>\n",
"Org Name: <highlight>{org_name}</highlight>\n",
"\n",
"<pagebreak><header2>Bots Connected</header2>\n",
"{bots_connected}",
"\n",
"<pagebreak><header2>Public Channels</header2>\n",
"{pub_channels}",
"\n",
"<pagebreak><header2>Event Types</header2>\n",
"{event_types}",
"\n",
"<pagebreak><header2>Access Levels</header2>\n",
"{access_levels}"
],
"de_DE": [
"Version: <highlight>Tyrbot {bot_ver}</highlight>\n",
"Name: <highlight><myname></highlight>\n",
"\n",
"Betriebssystem: <highlight>{os_ver}</highlight>\n",
"Python: <highlight>{python_ver}</highlight>\n",
"Datenbank: <highlight>{db_type}</highlight>\n",
"Arbeitsspeichernutzung: <highlight>{mem_usage} KB</highlight>\n",
"\n",
"Superadmin: <highlight>{superadmin}</highlight>\n",
"Freundesliste: <highlight>{bl_used}/{bl_size}</highlight>\n",
"Onlinezeit: <highlight>{uptime}</highlight>\n",
"Dimension: <highlight>{dim}</highlight>\n",
"\n",
"Org ID: <highlight>{org_id}</highlight>\n",
"Org Name: <highlight>{org_name}</highlight>\n",
"\n",
"<pagebreak><header2>Bots Connected</header2>\n",
"{bots_connected}",
"\n",
"<pagebreak><header2>Öffentliche Channel</header2>\n",
"{pub_channels}",
"\n",
"<pagebreak><header2>Event Typen</header2>\n",
"{event_types}",
"\n",
"<pagebreak><header2>Zugriffslevel</header2>\n",
"{access_levels}",
"\n"
]
},
"status_title": {
"en_US": "System Info",
"de_DE": "Systeminformationen"
},
"messagehub_title": {
"en_US": "Message Hub Subscriptions ({count})"
},
"messagehub_info": {
"en_US": "Destinations are listed below, along with the sources they are subscribed to."
},
"subscribed": {
"en_US": "Subscribed"
},
"unsubscribed": {
"en_US": "Unsubscribed"
},
"messagehub_edit_title": {
"en_US": "{destination} Subscriptions ({count})"
},
"messagehub_already_subscribed": {
"en_US": "Destination <highlight>{destination}</highlight> is already subscribed to source <highlight>{source}</highlight>."
},
"messagehub_not_subscribed": {
"en_US": "Destination <highlight>{destination}</highlight> is not subscribed to source <highlight>{source}</highlight>."
},
"messagehub_invalid_subscription": {
"en_US": "Destination <highlight>{destination}</highlight> cannot be subscribed to source <highlight>{source}</highlight>."
},
"messagehub_subscribe_success": {
"en_US": "Destination <highlight>{destination}</highlight> has been subscribed to source <highlight>{source}</highlight> successfully."
},
"messagehub_unsubscribe_success": {
"en_US": "Destination <highlight>{destination}</highlight> has been unsubscribed from source <highlight>{source}</highlight> successfully."
},
"destination_not_exist": {
"en_US": "Destination <highlight>{destination}</highlight> does not exist."
}
}
+110
View File
@@ -0,0 +1,110 @@
import hjson
from core.command_param_types import Any
from core.command_service import CommandService
from core.decorators import instance, command, event
from core.dict_object import DictObject
from core.logger import Logger
from core.setting_service import SettingService
from core.setting_types import BooleanSettingType, TextSettingType, NumberSettingType
from core.translation_service import TranslationService
@instance()
class SystemController:
SHUTDOWN_EVENT = "shutdown"
MESSAGE_SOURCE = "shutdown_notice"
def __init__(self):
self.logger = Logger(__name__)
def inject(self, registry):
self.bot = registry.get_instance("bot")
self.setting_service: SettingService = registry.get_instance("setting_service")
self.event_service = registry.get_instance("event_service")
self.character_service = registry.get_instance("character_service")
self.ts: TranslationService = registry.get_instance("translation_service")
self.message_hub_service = registry.get_instance("message_hub_service")
self.getresp = self.ts.get_response
def pre_start(self):
self.event_service.register_event_type(self.SHUTDOWN_EVENT)
self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)
def start(self):
self.ts.register_translation("module/system", self.load_system_msg)
self.setting_service.register_new(self.module_name, "expected_shutdown", True, BooleanSettingType(),
"Helps bot to determine if last shutdown was expected or due to a problem")
self.setting_service.register_new("core.system", "symbol", "!",
TextSettingType(["!", "#", "*", "@", "$", "+", "-"]),
"Symbol for executing bot commands")
self.setting_service.register_new("core.system", "org_channel_max_page_length", 7500,
NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
"Maximum size of blobs in org channel")
self.setting_service.register_new("core.system", "private_message_max_page_length", 7500,
NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
"Maximum size of blobs in private messages")
self.setting_service.register_new("core.system", "private_channel_max_page_length", 7500,
NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
"Maximum size of blobs in private channel")
self.setting_service.register_new("core.system", "accept_commands_from_slave_bots", False, BooleanSettingType(),
"Accept and respond to commands sent to slave bots (only applies if you have "
"added slave bots in the config)")
def load_system_msg(self):
with open("modules/core/system/system.msg", mode="r", encoding="utf-8") as f:
return hjson.load(f)
def expected_shutdown(self):
return self.setting_service.get("expected_shutdown")
@command(command="shutdown", params=[Any("reason", is_optional=True)], access_level="superadmin",
description="Shutdown the bot")
def shutdown_cmd(self, request, reason):
if request.channel not in [CommandService.ORG_CHANNEL, CommandService.PRIVATE_CHANNEL]:
request.reply(self._format_message(False, reason))
self.shutdown(False, reason)
@command(command="restart", params=[Any("reason", is_optional=True)], access_level="admin",
description="Restart the bot")
def restart_cmd(self, request, reason):
if request.channel not in [CommandService.ORG_CHANNEL, CommandService.PRIVATE_CHANNEL]:
request.reply(self._format_message(True, reason))
self.shutdown(True, reason)
@event(event_type="connect", description="Notify superadmin that bot has come online")
def connect_event(self, _, _1):
if self.expected_shutdown().get_value():
msg = self.getresp("module/system", "expected_online")
else:
self.logger.warning("The bot has recovered from an unexpected shutdown or restart")
msg = self.getresp("module/system", "unexpected_online")
self.bot.send_org_message(msg, fire_outgoing_event=False)
self.bot.send_private_channel_message(msg, fire_outgoing_event=False)
self.expected_shutdown().set_value(False)
def shutdown(self, should_restart, reason=None):
self.event_service.fire_event(self.SHUTDOWN_EVENT, DictObject({"restart": should_restart, "reason": reason}))
# set expected flag
self.expected_shutdown().set_value(True)
self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, None,
self._format_message(should_restart, reason))
if should_restart:
self.bot.restart()
else:
self.bot.shutdown()
def _format_message(self, restart, reason):
if restart:
if reason:
return self.getresp("module/system", "restart") + self.getresp("module/system", "reason",
{"reason": reason})
return self.getresp("module/system", "restart") + ".."
if reason:
return self.getresp("module/system", "shutdown") + self.getresp("module/system", "reason",
{"reason": reason})
return self.getresp("module/system", "shutdown") + ".."
+134
View File
@@ -0,0 +1,134 @@
import html
import os
import platform
import sys
import time
import psutil
from core.chat_blob import ChatBlob
from core.command_param_types import Any, Character
from core.decorators import instance, command
from core.util import Util
@instance()
class UtilController:
def inject(self, registry):
self.bot = registry.get_instance("bot")
self.db = registry.get_instance("db")
self.util: Util = registry.get_instance("util")
self.command_service = registry.get_instance("command_service")
self.buddy_service = registry.get_instance("buddy_service")
self.access_service = registry.get_instance("access_service")
self.event_service = registry.get_instance("event_service")
self.public_channel_service = registry.get_instance("public_channel_service")
self.getresp = registry.get_instance("translation_service").get_response
@command(command="checkaccess", params=[Character("character")], access_level="moderator",
description="Check access level for a character", sub_command="other")
def checkaccess_other_cmd(self, _, char):
if not char.char_id:
return self.getresp("global", "char_not_found", {"char": char.name})
return self.getresp("module/system", "check_access",
{"char": char.name,
"rank_main": char.access_level["label"]})
@command(command="checkaccess", params=[], access_level="member",
description="Check your access level")
def checkaccess_cmd(self, request):
char = request.sender
return self.getresp("module/system", "check_access",
{"char": char.name,
"rank_main": char.access_level["label"]})
@command(command="macro", params=[Any("command1|command2|command3...")], access_level="member",
description="Execute multiple commands at once")
def macro_cmd(self, request, commands):
commands = commands.split("|")
for command_str in commands:
self.command_service.process_command(
self.command_service.trim_command_symbol(command_str),
request.channel,
request.sender.char_id,
request.reply,
request.conn)
@command(command="echo", params=[Any("message")], access_level="member",
description="Echo back a message")
def echo_cmd(self, _, message):
return html.escape(message)
@command(command="showcommand", params=[Character("character"), Any("message")], access_level="admin",
description="Show command output to another character")
def showcommand_cmd(self, request, char, command_str):
if not char.char_id:
return self.getresp("global", "char_not_found", {"char": char.name})
self.bot.send_private_message(char.char_id, self.getresp("module/system", "show_output_target",
{"sender": request.sender.name,
"cmd": command_str}))
self.command_service.process_command(
self.command_service.trim_command_symbol(command_str),
request.channel,
request.sender.char_id,
lambda msg: self.bot.send_private_message(char.char_id, msg),
request.conn)
return self.getresp("module/system", "show_output_self",
{"target": char.name,
"cmd": command_str})
@command(command="system", params=[], access_level="admin",
description="Show system information")
def system_cmd(self, _):
pub_channels = ""
event_types = ""
access_levels = ""
bots_connected = ""
for _id, conn in self.bot.conns.items():
bots_connected += f"{_id} - {conn.char_name} ({conn.char_id})\n"
for channel_id, name in self.public_channel_service.get_all_public_channels().items():
pub_channels += "%s - <highlight>%d</highlight>\n" % (name, channel_id)
for event_type in self.event_service.get_event_types():
event_types += "%s\n" % event_type
for access_level in self.access_service.get_access_levels():
access_levels += "%s (%d)\n" % (access_level["label"], access_level["level"])
blob = self.getresp("module/system", "status_blob", {
"bot_ver": f"{self.bot.major_version}.{self.bot.minor_version}",
"os_ver": platform.system() + " " + platform.release(),
"python_ver": str(sys.version_info.major)
+ "." + str(sys.version_info.minor)
+ "." + str(sys.version_info.micro)
+ "." + sys.version_info.releaselevel,
"db_type": self.db.type if not self.db.MARIADB else f"{self.db.MARIADB} with "
f"{self.db.pool_size} active connections",
"mem_usage": self.util.format_number(psutil.Process(os.getpid()).memory_info().rss / 1024),
"superadmin": "Not Set",
"bl_used": self.buddy_service.get_buddy_list_size(),
"bl_size": self.buddy_service.buddy_list_size,
"uptime": self.util.time_to_readable(int(time.time()) - self.bot.start_time, max_levels=None),
"dim": self.bot.dimension,
"org_id": self.public_channel_service.org_id,
"org_name": self.public_channel_service.org_name,
"bots_connected": bots_connected,
"pub_channels": pub_channels,
"event_types": event_types,
"access_levels": access_levels
})
return ChatBlob(self.getresp("module/system", "status_title"), blob)
@command(command="htmldecode", params=[Any("command")], access_level="member",
description="Decode html entities from a command before passing to the bot for execution")
def htmldecode_cmd(self, request, command_str):
self.command_service.process_command(html.unescape(command_str), request.channel, request.sender.char_id,
request.reply, request.conn)