Initial Release of IGNCore version 2.5
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
import time
|
||||
|
||||
from core import command_request
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Character, Options
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.event_service import EventService
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
from core.util import Util
|
||||
from modules.core.accounting.preference_controller import PreferenceController
|
||||
from modules.core.accounting.services.access_service import AccessService
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
|
||||
|
||||
@instance()
|
||||
class AccountController:
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.buddy_service = registry.get_instance("buddy_service")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.event_service: EventService = registry.get_instance("event_service")
|
||||
self.getresp = self.ts.get_response
|
||||
self.pork_service: PorkService = registry.get_instance("pork_service")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.preferences: PreferenceController = registry.get_instance("preference_controller")
|
||||
self.access_service: AccessService = registry.get_instance("access_service")
|
||||
|
||||
@command(command="account", params=[], access_level="member",
|
||||
description="View your account")
|
||||
def account(self, request: command_request):
|
||||
out = self.show_account(request.sender.char_id)
|
||||
self.bot.send_mass_message(request.sender.char_id, out)
|
||||
|
||||
@command(command="accounts", params=[Const('online', is_optional=True)], access_level="moderator",
|
||||
description="View all accounts")
|
||||
def accounts(self, request: command_request, online):
|
||||
accs = self.account_service.get_all_members(True if online else False)
|
||||
main = 0
|
||||
main_name = ""
|
||||
entries = []
|
||||
for entry in accs:
|
||||
out = ""
|
||||
if main != entry.main:
|
||||
main = entry.main
|
||||
out += f"\n<{entry.faction.lower()}>{entry.name}</{entry.faction.lower()}>: " \
|
||||
f"[{self.text.make_tellcmd('D', f'account {entry.name}')}]\n"
|
||||
main_name = entry.name
|
||||
n = f"<red>N</red>" if entry.last_seen == 0 else ""
|
||||
out += f"<tab> {self.util.get_prof_icon(entry.profession)}" \
|
||||
f" {self.text.zfill(entry.level, 220)}/<green>{self.text.zfill(entry.ai_level, 30)}</green> " \
|
||||
f"<{entry.faction.lower()}>{entry.name}</{entry.faction.lower()}> {n}\n"
|
||||
entries.append([main_name, out])
|
||||
out = ""
|
||||
msg = sorted(entries, key=lambda k: k[0])
|
||||
for _, mess in msg:
|
||||
out += mess
|
||||
self.bot.send_mass_message(request.sender.char_id, ChatBlob("All accounts", out))
|
||||
|
||||
@command(command="points", params=[], access_level="member",
|
||||
description="View your points")
|
||||
def points(self, request: command_request):
|
||||
alts = self.account_service.get_account(request.sender.char_id)
|
||||
self.bot.send_mass_message(request.sender.char_id,
|
||||
f"So far, you've piled <notice>{alts.points}</notice> points up.")
|
||||
|
||||
@command(command="account", params=[Character("user")],
|
||||
access_level="moderator",
|
||||
sub_command="moderate", description="Lookup account of another char")
|
||||
def display_account(self, _, user):
|
||||
if not user.char_id:
|
||||
return f"Character <highlight>{user.name}</highlight> not found."
|
||||
return self.show_account(user.char_id, True)
|
||||
|
||||
@command(command="account", params=[Const("add"), Character("char")],
|
||||
access_level="moderator",
|
||||
sub_command="moderate", description="Create a new account for given character")
|
||||
def add_account(self, request: command_request, _, user):
|
||||
if not user.char_id:
|
||||
return f"Character <highlight>{user.name}</highlight> not found."
|
||||
if self.account_service.get_account(user.char_id):
|
||||
self.account_service.account_enable(user.char_id)
|
||||
self.buddy_service.add_buddy(user.char_id, "member")
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Opened Account for <highlight>{user.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
self.account_service.add_log(user.char_id, "system",
|
||||
f"Account has been reopened by <highlight>{request.sender.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
return f"There's already an account for character <highlight>{user.name}</highlight>; It has been enabled."
|
||||
count = self.account_service.create_users([(user.char_id, user.char_id, 0, time.time(), time.time())])
|
||||
if count > 0:
|
||||
self.buddy_service.add_buddy(user.char_id, "member")
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Created Account for <highlight>{user.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
self.account_service.add_log(user.char_id, "system",
|
||||
f"Account has been created by <highlight>{request.sender.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
self.bot.send_mass_message(user.char_id,
|
||||
f"Your Account has been <green>opened</green> by "
|
||||
f"<highlight>{request.sender.name}</highlight>.")
|
||||
return f"Character <highlight>{user.name}'s</highlight> Account has been created!"
|
||||
else:
|
||||
return f"There's already an account for character <highlight>{user.name}</highlight>."
|
||||
|
||||
@command(command="account", params=[Const("rem"), Character("char")],
|
||||
access_level="moderator",
|
||||
sub_command="moderate", description="Disable a new account for given character")
|
||||
def rem_account(self, request: command_request, _, user):
|
||||
if not user.char_id:
|
||||
return f"Character <highlight>{user.name}</highlight> not found."
|
||||
if not self.access_service.has_sufficient_access_level(request.sender.char_id, user.char_id):
|
||||
return f"The user <highlight>{user.name}</highlight> has a higher rank then you, you cant do that."
|
||||
disabled = self.account_service.account_disable(user.char_id)
|
||||
if disabled:
|
||||
self.buddy_service.remove_buddy(user.char_id, "member")
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Disabled Account of <highlight>{user.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
self.account_service.add_log(user.char_id, "system",
|
||||
f"Account disabled by <highlight>{request.sender.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
return f"Character <highlight>{user.name}'s</highlight> Account has been disabled!"
|
||||
else:
|
||||
return f"There's no active account for character <highlight>{user.name}</highlight>."
|
||||
|
||||
@command(command="account", params=[Const("purge"), Character("char")],
|
||||
access_level="admin",
|
||||
sub_command="modify", description="Purge a user from the database")
|
||||
def purge_account(self, request: command_request, _, user):
|
||||
if not user.char_id:
|
||||
return f"Character <highlight>{user.name}</highlight> not found."
|
||||
if not self.access_service.has_sufficient_access_level(request.sender.char_id, user.char_id):
|
||||
return f"The user <highlight>{user.name}</highlight> has a higher rank then you, you cant do that."
|
||||
account = self.account_service.get_alts(user.char_id)
|
||||
if account:
|
||||
for alt in account:
|
||||
self.buddy_service.remove_buddy(alt.char_id, "member")
|
||||
self.db.exec("DELETE FROM notes where char_id=?", [alt.char_id])
|
||||
self.db.exec("DELETE FROM account where main=?", [account[0].main])
|
||||
self.db.exec("DELETE FROM account_log where char_id=?", [account[0].main])
|
||||
self.db.exec("DELETE FROM command_usage where char_id=?", [account[0].main])
|
||||
self.db.exec("DELETE FROM pending_accounts where main=? or alt=?", [account[0].main, account[0].main])
|
||||
self.db.exec("DELETE FROM mail where recipient=? or sender=?", [account[0].main, account[0].main])
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Disabled Account of <highlight>{user.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
return f"Character <highlight>{user.name}'s</highlight> Account has been purged!"
|
||||
else:
|
||||
return f"There's no active account for character <highlight>{user.name}</highlight>."
|
||||
|
||||
@command(command="account",
|
||||
params=[Const("rank"), Options(["admin", "moderator", "council", "leader"]), Character("user")],
|
||||
access_level="admin", sub_command="modify", description="Add a rank to an Account")
|
||||
def set_rank(self, request: command_request, _, rank: str, user):
|
||||
if not user.char_id:
|
||||
return f"Character <highlight>{user.name}</highlight> not found."
|
||||
user = self.account_service.get_main(user.char_id)
|
||||
if not user:
|
||||
return f"No account for <highlight>{user.name}</highlight> found."
|
||||
# is Superadmin
|
||||
if rank == "admin" and \
|
||||
self.account_service.check_superadmin(self.account_service.get_main(request.sender.char_id).char_id):
|
||||
if "admin" in self.account_service.get_ranks(user.char_id):
|
||||
return f"<highlight>{user.name}</highlight> already has the rank " \
|
||||
f"<highlight>{rank.capitalize()}</highlight>."
|
||||
else:
|
||||
self.account_service.add_rank(user.char_id, "admin")
|
||||
return f"<highlight>{user.name}</highlight> got promoted to the rank " \
|
||||
f"<highlight>{rank.capitalize()}</highlight>."
|
||||
# is Admin
|
||||
if self.account_service.check_admin(self.account_service.get_main(request.sender.char_id).char_id) \
|
||||
or self.account_service.check_superadmin(self.account_service.get_main(request.sender.char_id).char_id):
|
||||
if rank.lower() in self.account_service.get_ranks(user.char_id):
|
||||
return f"<highlight>{user.name}</highlight> already has the rank " \
|
||||
f"<highlight>{rank.capitalize()}</highlight>."
|
||||
else:
|
||||
self.account_service.add_rank(user.char_id, rank.lower())
|
||||
return f"<highlight>{user.name}</highlight> got promoted to the rank " \
|
||||
f"<highlight>{rank.capitalize()}</highlight>."
|
||||
return "You can't do that."
|
||||
|
||||
@command(command="account",
|
||||
params=[Options(["delrank", "remrank"]), Options(["admin", "moderator", "council", "leader"]),
|
||||
Character("user")],
|
||||
access_level="admin", sub_command="modify", description="Remove a rank from an Account")
|
||||
def rem_rank(self, request: command_request, _, rank: str, user):
|
||||
if not user.char_id:
|
||||
return f"Character <highlight>{user.name}</highlight> not found."
|
||||
user = self.account_service.get_main(user.char_id)
|
||||
# is Superadmin
|
||||
if rank == "admin" and self.account_service.check_superadmin(
|
||||
self.account_service.get_main(request.sender.char_id).char_id):
|
||||
if "admin" not in self.account_service.get_ranks(user.char_id):
|
||||
return f"<highlight>{user.name}</highlight> is not in the " \
|
||||
f"<highlight>{rank.capitalize()}</highlight> group."
|
||||
else:
|
||||
self.account_service.del_rank(user.char_id, "admin")
|
||||
return f"<highlight>{user.name}</highlight> is nolonger in the " \
|
||||
f"<highlight>{rank.capitalize()}</highlight> group."
|
||||
# is Admin
|
||||
elif self.account_service.check_admin(self.account_service.get_main(request.sender.char_id).char_id):
|
||||
if rank.lower() not in self.account_service.get_ranks(user.char_id):
|
||||
return f"<highlight>{user.name}</highlight> is not in the " \
|
||||
f"<highlight>{rank.capitalize()}</highlight> group."
|
||||
else:
|
||||
self.account_service.del_rank(user.char_id, rank.lower())
|
||||
return f"<highlight>{user.name}</highlight> is nolonger in the " \
|
||||
f"<highlight>{rank.capitalize()}</highlight> group."
|
||||
|
||||
def show_account(self, char_id, mod=False) -> str:
|
||||
|
||||
alts = self.account_service.get_alts(char_id)
|
||||
if len(alts) < 1:
|
||||
return "No Account registered for this user."
|
||||
prefs = self.preferences.get_pref_view_small(alts[0])
|
||||
response = f"<header>{alts[0].name}'s Account</header>\n\n" \
|
||||
f" Owner: <notice>{alts[0].name}</notice> ({alts[0].char_id}) " \
|
||||
f"[{self.text.make_tellcmd('W', f'whois {alts[0].name}')}] " \
|
||||
f"[{self.text.make_tellcmd('Alts', f'alts {alts[0].name}')}] " \
|
||||
f"({len(alts)})\n"
|
||||
if not mod:
|
||||
response += f" Options: {prefs}\n"
|
||||
response += f" Points: <notice>{alts[0].points}</notice>\n"
|
||||
access_levels = {"Member": self.account_service.check_member(alts[0].char_id),
|
||||
"Officer": self.account_service.check_officer(alts[0].char_id),
|
||||
"General": self.account_service.check_general(alts[0].char_id),
|
||||
"President": self.account_service.check_president(alts[0].char_id),
|
||||
"Leader": self.account_service.check_leader(alts[0].char_id),
|
||||
"Council": self.account_service.check_council(alts[0].char_id),
|
||||
"Moderator": self.account_service.check_moderator(alts[0].char_id),
|
||||
"Admin": self.account_service.check_admin(alts[0].char_id)}
|
||||
perms = []
|
||||
for key, value in access_levels.items():
|
||||
if value:
|
||||
perms.append(key)
|
||||
response += f" Status: {'<green>Open</green>' if alts[0].disabled == 0 else '<red>Closed</red>'}\n"
|
||||
response += f" Created at: <notice>{self.util.format_datetime(alts[0].created)}</notice>\n"
|
||||
response += f" Permissions: <notice>{', '.join(perms)}</notice>\n"
|
||||
if alts[0].discord_joined == 1:
|
||||
joined = '(Joined server)'
|
||||
else:
|
||||
joined = '(Left server)' if alts[0].discord_invite != '' else 'Never joined'
|
||||
response += f" Discord: <notice>{alts[0].discord_handle}</notice> {joined}\n\n"
|
||||
log_types = ['Points', 'Loot', 'Raid', 'Public', 'Admin', 'System']
|
||||
response += " Logs: ["
|
||||
for i in log_types:
|
||||
response += f" {self.text.make_tellcmd(i, f'account log {i.lower()} {alts[0].name} 100')}"
|
||||
response += " ]\n\n"
|
||||
|
||||
response += "<header>Last 20 Logs</header>\n"
|
||||
rows = self.account_service.get_logs(alts[0].char_id, limit=20)
|
||||
for i in rows:
|
||||
response += self.account_service.format_entry(i)
|
||||
return self.text.format_page(f"{alts[0].name}'s Account", response)
|
||||
@@ -0,0 +1,82 @@
|
||||
from core import command_request
|
||||
from core.aochat.BaseModule import BaseModule
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Options, Int, Character, Any
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.event_service import EventService
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.text import Text
|
||||
from core.util import Util
|
||||
from modules.core.accounting.preference_controller import PreferenceController
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
|
||||
|
||||
@instance()
|
||||
class AccountLogController(BaseModule):
|
||||
|
||||
# noinspection DuplicatedCode
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.buddy_service = registry.get_instance("buddy_service")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.event_service: EventService = registry.get_instance("event_service")
|
||||
self.pork_service: PorkService = registry.get_instance("pork_service")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.preferences: PreferenceController = registry.get_instance("preference_controller")
|
||||
|
||||
@command(command="account", params=[Const("log"), Options(["public", "raid", "loot"]),
|
||||
Character("char"), Int("count", is_optional=True)], access_level="member",
|
||||
description="Lookup logentries of a Account",
|
||||
sub_command="public")
|
||||
def account_log_1(self, _, _1, log_type, user, count):
|
||||
main = self.account_service.get_main(user.char_id).char_id
|
||||
entries = self.account_service.get_logs(main, log_type=log_type, limit=count or 25)
|
||||
return ChatBlob(f"Logentries for {user.name} in the category {log_type}", self.display_logs(entries))
|
||||
|
||||
@command(command="account",
|
||||
params=[Const("log"), Options(["admin", "system", "points"]),
|
||||
Character("char"), Int("count", is_optional=True)],
|
||||
access_level="moderator",
|
||||
description="Lookup logentries of a Account",
|
||||
sub_command="moderate")
|
||||
def account_log_2(self, _, _1, log_type, user, count):
|
||||
main = self.account_service.get_main(user.char_id).char_id
|
||||
entries = self.account_service.get_logs(main, log_type=log_type, limit=count or 25)
|
||||
return ChatBlob(f"Logentries for {user.name} in the category {log_type}", self.display_logs(entries))
|
||||
|
||||
@command(command="account", params=[Const("addlog"), Options(["admin", "public"]),
|
||||
Character("char"), Any("text")], access_level="moderator",
|
||||
description="Add a log entry to an Account",
|
||||
sub_command="moderate")
|
||||
def account_add_log(self, request: command_request, _, log_type, user, text):
|
||||
main = self.account_service.get_main(user.char_id).char_id
|
||||
self.account_service.add_log(main, log_type, text, request.sender.char_id)
|
||||
|
||||
# noinspection LongLine
|
||||
@command(command="account", params=[Const("log"), Const("id"), Int("id")], access_level="moderator",
|
||||
description="View a logentry of an user",
|
||||
sub_command="moderate")
|
||||
def account_view_log(self, _, _1, _2, log_id):
|
||||
entry = self.account_service.get_log_by_id(log_id)
|
||||
if entry:
|
||||
return ChatBlob(f"Information about Logentry {log_id}",
|
||||
f"ID: <highlight>{entry.log_id}</highlight>\n"
|
||||
f"Type: <highlight>{entry.type.capitalize()}</highlight>\n"
|
||||
f"Affected Main: <highlight>{self.character_service.resolve_char_to_name(entry.char_id)}</highlight>\n"
|
||||
f"Responsible Leader: <highlight>{self.character_service.resolve_char_to_name(entry.leader_id)}</highlight>\n"
|
||||
f"Points: <highlight>{f'<green>+{entry.delta}</green>' if entry.delta > 0 else f'<red>{entry.delta}</red>'}</highlight>\n"
|
||||
f"Message: <grey>{entry.reason}</grey>\n"
|
||||
f"Logging Time: <highlight>{self.util.format_datetime(entry.created_at)}</highlight>")
|
||||
else:
|
||||
return f"No Logentry with ID {id} found."
|
||||
|
||||
def display_logs(self, entries) -> str:
|
||||
blob = ""
|
||||
for entry in entries:
|
||||
blob += self.account_service.format_entry(entry)
|
||||
return blob
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"list": {
|
||||
"en_US": "Alts of {char} ({amount})",
|
||||
"de_DE": "Alts von {char} ({amount})"
|
||||
},
|
||||
"new_main": {
|
||||
"en_US": "<highlight>{char}</highlight> character has been set as your main. Your preferences have been reset.",
|
||||
"de_DE": "Dein neuer Main ist <highlight>{char}</highlight>."
|
||||
},
|
||||
"not_an_alt": {
|
||||
"en_US": "Error! This character cannot be set as your main since you do not have any alts",
|
||||
"de_DE": "Error! Da du keine Alts hast, kannst du auch keinen Main Charakter setzen."
|
||||
},
|
||||
"already_main": {
|
||||
"en_US": "Error! This character is already set as your main.",
|
||||
"de_DE": "Error! Dieser Charakter ist bereits dein Main."
|
||||
},
|
||||
"add_fail_self": {
|
||||
"en_US": "Error! You cannot register yourself as an alt.",
|
||||
"de_DE": "Error! Du kannst dich nicht als dein eigener Alt registrieren."
|
||||
},
|
||||
"add_success_target": {
|
||||
"en_US": "<highlight>{char}</highlight> has added you as an alt.",
|
||||
"de_DE": "<highlight>{char}</highlight> hat dich als seinen Alt hinzugefügt."
|
||||
},
|
||||
"add_success_self": {
|
||||
"en_US": "<highlight>{char}</highlight> has been added as your alt.",
|
||||
"de_DE": "<highlight>{char}</highlight> wurde zu deinen Alts hinzugefügt."
|
||||
},
|
||||
"add_fail_already": {
|
||||
"en_US": "Error! <highlight>{char}</highlight> already has alts.",
|
||||
"de_DE": "Error! <highlight>{char}</highlight> hat bereits alts."
|
||||
},
|
||||
"rem_success": {
|
||||
"en_US": "<highlight>{char}</highlight> has been removed as your alt.",
|
||||
"de_DE": "<highlight>{char}</highlight> wurde von deinen Alts entfernt."
|
||||
},
|
||||
"rem_fail_not": {
|
||||
"en_US": "Error! <highlight>{char}</highlight> is not your alt.",
|
||||
"de_DE": "Error! <highlight>{char}</highlight> ist kein Alt von dir."
|
||||
},
|
||||
"rem_fail_main": {
|
||||
"en_US": "Error! You cannot remove your main.",
|
||||
"de_DE": "Error! Du kannst deinen Main nicht entfernen."
|
||||
},
|
||||
"altadmin_add_same": {
|
||||
"en_US": "Error! alt and main are identical.",
|
||||
"de_DE": "Error! Der Alt und Main Charakter sind identisch."
|
||||
},
|
||||
"altadmin_add_success": {
|
||||
"en_US": "The Toon <highlight>{alt}</highlight> got added as an alt of <highlight>{main}</highlight> successfully.",
|
||||
"de_DE": "Der Charakter <highlight>{alt}</highlight> ist nun ein Alt von <highlight>{main}</highlight>."
|
||||
},
|
||||
"altadmin_rem_success": {
|
||||
"en_US": "<highlight>{alt}</highlight> is nolonger an alt of <highlight>{main}</highlight>.",
|
||||
"de_DE": "<highlight>{alt}</highlight> ist nun kein Alt von <highlight>{main}</highlight> mehr."
|
||||
},
|
||||
"altadmin_rem_fail_not": {
|
||||
"en_US": "Error! <highlight>{alt}</highlight> is not an alt of <highlight>{main}</highlight>.",
|
||||
"de_DE": "Error! <highlight>{alt}</highlight> ist kein Alt von <highlight>{main}</highlight>."
|
||||
},
|
||||
"altadmin_rem_fail_main": {
|
||||
"en_US": "Error! Main Characters may not get removed from their alt list.",
|
||||
"de_DE": "Error! Du kannst keinen Main von seiner Altliste entfernen."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import re
|
||||
|
||||
import hjson
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Options, Character, Multiple, Any
|
||||
from core.decorators import instance, command
|
||||
from core.dict_object import DictObject
|
||||
from core.setting_service import SettingService
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
from core.tyrbot import Tyrbot
|
||||
from modules.core.accounting.register_controller import RegisterController
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
|
||||
|
||||
@instance()
|
||||
class AltsController:
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.buddy_service = registry.get_instance("buddy_service")
|
||||
self.util = registry.get_instance("util")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.character_service = registry.get_instance("character_service")
|
||||
self.getresp = self.ts.get_response
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.register_controller: RegisterController = registry.get_instance("register_controller")
|
||||
|
||||
def start(self):
|
||||
self.ts.register_translation("module/alts", self.load_alts_msg)
|
||||
|
||||
def load_alts_msg(self) -> dict:
|
||||
with open("modules/core/accounting/alts.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@command(command="alts", params=[], access_level="member",
|
||||
description="Show your alts")
|
||||
def alts_list_cmd(self, request):
|
||||
alts = self.account_service.get_alts(request.sender.char_id)
|
||||
blob = self.format_alt_list(alts)
|
||||
|
||||
return ChatBlob(self.getresp("module/alts", "list", {"char": alts[0].name, "amount": len(alts)}), blob)
|
||||
|
||||
@command(command="alts", params=[Const("setmain")], access_level="member", description="Set a new main",
|
||||
extended_description="You must run this from the character you want to be your new main")
|
||||
def alts_setmain_cmd(self, request, _):
|
||||
msg, result = self.account_service.set_as_main(request.sender.char_id)
|
||||
|
||||
if result:
|
||||
return self.getresp("module/alts", "new_main", {"char": request.sender.name})
|
||||
elif msg == "not_an_alt":
|
||||
return self.getresp("module/alts", "not_an_alt")
|
||||
elif msg == "already_main":
|
||||
return self.getresp("module/alts", "already_main")
|
||||
else:
|
||||
raise Exception("Unknown msg: " + msg)
|
||||
|
||||
@command(command="alts", params=[Const("add"), Multiple(Character("character"))], access_level="member",
|
||||
description="Add an alt")
|
||||
def alts_add_cmd(self, request, _, alt_chars):
|
||||
responses = []
|
||||
for alt_char in alt_chars:
|
||||
if not alt_char.char_id:
|
||||
responses.append(self.getresp("global", "char_not_found", {"char": alt_char.name}))
|
||||
continue
|
||||
elif alt_char.char_id == request.sender.char_id:
|
||||
responses.append(self.getresp("module/alts", "add_fail_self"))
|
||||
continue
|
||||
manual = self.setting_service.get_value("alt_verification") == "1"
|
||||
if manual:
|
||||
responses.append(self.register_controller.register_alt(request, _, alt_char))
|
||||
continue
|
||||
msg, result = self.account_service.add_alt(request.sender.char_id, alt_char.char_id)
|
||||
|
||||
if result:
|
||||
self.bot.send_mass_message(alt_char.char_id, self.getresp("module/alts", "add_success_target",
|
||||
{"char": request.sender.name}))
|
||||
responses.append(self.getresp("module/alts", "add_success_self", {"char": alt_char.name}))
|
||||
elif msg == "another_main":
|
||||
responses.append(self.getresp("module/alts", "add_fail_already", {"char": alt_char.name}))
|
||||
else:
|
||||
raise Exception("Unknown msg: " + msg)
|
||||
|
||||
return "\n".join(responses)
|
||||
|
||||
@command(command="alts", params=[Options(["rem", "remove"]), Character("character")], access_level="member",
|
||||
description="Remove an alt")
|
||||
def alts_remove_cmd(self, request, _, alt_char):
|
||||
manual = self.setting_service.get_value("alt_verification") == "1"
|
||||
if manual:
|
||||
return "This command is disabled, you cannot remove alts."
|
||||
|
||||
if not alt_char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": alt_char.name})
|
||||
|
||||
msg, result = self.account_service.remove_alt(request.sender.char_id, alt_char.char_id)
|
||||
if result:
|
||||
return self.getresp("module/alts", "rem_success", {"char": alt_char.name})
|
||||
elif msg == "not_alt":
|
||||
return self.getresp("module/alts", "rem_fail_not", {"char": alt_char.name})
|
||||
elif msg == "remove_main":
|
||||
return self.getresp("module/alts", "rem_fail_main")
|
||||
else:
|
||||
raise Exception("Unknown msg: " + msg)
|
||||
|
||||
@command(command="alts", params=[Character("character")], access_level="member",
|
||||
description="Show alts of another character", sub_command="show")
|
||||
def alts_list_other_cmd(self, _, char):
|
||||
if not char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
|
||||
alts = self.account_service.get_alts(char.char_id)
|
||||
blob = self.format_alt_list(alts)
|
||||
if len(alts) < 1:
|
||||
return "No alts found"
|
||||
return ChatBlob(self.getresp("module/alts", "list", {"char": alts[0].name, "amount": len(alts)}), blob)
|
||||
|
||||
@command(command="altadmin", params=[Const("add"), Character("main"), Any("alts")],
|
||||
access_level="admin", description="Add alts to Main")
|
||||
def altadmin_add_cmd(self, _, _1, main, altlist):
|
||||
alts = re.findall(r"([^ ]+)", altlist)
|
||||
notfound = []
|
||||
done = []
|
||||
failed = []
|
||||
gotalts = []
|
||||
if alts:
|
||||
for alt in alts:
|
||||
char_id = self.character_service.resolve_char_to_id(alt)
|
||||
if not char_id:
|
||||
notfound.append(alt)
|
||||
continue
|
||||
alt = DictObject({"char_id": char_id, "name": alt})
|
||||
if main.char_id == alt.char_id:
|
||||
failed.append(self.getresp("module/alts", "altadmin_add_same"))
|
||||
|
||||
msg, result = self.account_service.add_alt(main.char_id, alt.char_id)
|
||||
if result:
|
||||
done.append(alt.name)
|
||||
elif msg == "another_main":
|
||||
gotalts.append(alt.name)
|
||||
reply = ""
|
||||
if len(done) > 0:
|
||||
reply += self.getresp("module/alts", "altadmin_add_success",
|
||||
{"alt": ", ".join(done),
|
||||
"main": main.name})
|
||||
if len(notfound) > 0:
|
||||
reply += "\n" + self.getresp("global", "char_not_found", {"char": ", ".join(notfound)})
|
||||
if len(gotalts) > 0:
|
||||
reply += "\n" + self.getresp("module/alts", "add_fail_already", {"char": ", ".join(gotalts)})
|
||||
reply += "\n".join(failed)
|
||||
return reply
|
||||
|
||||
@command(command="altadmin", params=[Options(["rem", "remove"]), Character("main"), Character("alt")],
|
||||
access_level="admin",
|
||||
description="Remove alts of main")
|
||||
def altadmin_remove_cmd(self, _, _1, main, alt):
|
||||
if not main.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": main.name})
|
||||
if not alt.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": alt.name})
|
||||
|
||||
msg, result = self.account_service.remove_alt(main.char_id, alt.char_id)
|
||||
|
||||
if result:
|
||||
return self.getresp("module/alts", "altadmin_rem_success", {"alt": alt.name, "main": main.name})
|
||||
elif msg == "not_alt":
|
||||
return self.getresp("module/alts", "altadmin_rem_fail_not", {"alt": alt.name, "main": main.name})
|
||||
elif msg == "remove_main":
|
||||
return self.getresp("module/alts", "altadmin_rem_fail_main")
|
||||
else:
|
||||
raise Exception("Unknown msg: " + msg)
|
||||
|
||||
def format_alt_list(self, alts) -> str:
|
||||
blob = ""
|
||||
for alt in alts:
|
||||
name = f"{alt.name}"
|
||||
blob += f"{self.util.get_prof_icon(alt.profession)} " \
|
||||
f"<yellow>{self.text.zfill(alt.level, 220)}</yellow>:" \
|
||||
f"<green>{self.text.zfill(alt.ai_level, 30)}</green> " \
|
||||
f":: <{alt.faction.lower()}>{name}</{alt.faction.lower()}> " \
|
||||
f"[<{alt.faction.lower()}>{alt.org_name}</{alt.faction.lower()}> - " \
|
||||
f"<highlight>{alt.org_rank_id + 1}</highlight>]"
|
||||
if self.buddy_service.is_online(alt.char_id):
|
||||
blob += " [<green>Online</green>]"
|
||||
blob += "\n"
|
||||
return blob
|
||||
@@ -0,0 +1,41 @@
|
||||
from core import command_request
|
||||
from core.command_param_types import Options, Int, Character, Any
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.event_service import EventService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.text import Text
|
||||
from core.util import Util
|
||||
from modules.core.accounting.preference_controller import PreferenceController
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
|
||||
|
||||
@instance()
|
||||
class PointsController:
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.buddy_service = registry.get_instance("buddy_service")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.character_service = registry.get_instance("character_service")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.event_service: EventService = registry.get_instance("event_service")
|
||||
self.pork_service: PorkService = registry.get_instance("pork_service")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.preferences: PreferenceController = registry.get_instance("preference_controller")
|
||||
|
||||
@command(command="account",
|
||||
params=[Options(["give", "take"]), Int("points"), Character("char"), Any("reason")],
|
||||
access_level="admin",
|
||||
description="Give or take points from an Account",
|
||||
sub_command="modify")
|
||||
def account_add_mod_pts(self, request: command_request, option, amount, user, reason):
|
||||
if option == "take":
|
||||
amount = -amount
|
||||
self.account_service.add_pts(user.char_id, amount, reason, request.sender.char_id)
|
||||
|
||||
def display_logs(self, entries):
|
||||
blob = ""
|
||||
for entry in entries:
|
||||
blob += self.account_service.format_entry(entry)
|
||||
return blob
|
||||
@@ -0,0 +1,107 @@
|
||||
from core.buddy_service import BuddyService
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_alias_service import CommandAliasService
|
||||
from core.command_param_types import Const, Options, Character
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
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.tyrbot import Tyrbot
|
||||
from core.util import Util
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
from modules.core.discord.discord_controller import DiscordController
|
||||
|
||||
|
||||
@instance()
|
||||
class PreferenceController:
|
||||
|
||||
def inject(self, registry):
|
||||
self.logger = Logger(__name__)
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.pork: PorkService = registry.get_instance("pork_service")
|
||||
self.org_pork: PorkService = registry.get_instance("org_pork_service")
|
||||
self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
|
||||
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.discord: DiscordController = registry.get_instance("discord_controller")
|
||||
self.job_scheduler = registry.get_instance("job_scheduler")
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("prefs", "preferences")
|
||||
self.command_alias_service.add_alias("autoinvite", "preferences set autoinvite")
|
||||
|
||||
@command(command="preferences", params=[], description="View your Preferences", access_level="member")
|
||||
def show_prefs(self, request):
|
||||
account = self.account_service.get_account(request.sender.char_id)
|
||||
if not account:
|
||||
return "You dont have any preferences you could manage...."
|
||||
return ChatBlob("Your Preferences", self.get_pref_view_full(account))
|
||||
|
||||
@command(command="preferences",
|
||||
params=[Const("set"),
|
||||
Options(["news", "raidinvite", "subtilespam", "autoinvite", "raidspam"]),
|
||||
Options(["on", "off", "yes", "no"])],
|
||||
description="Change your Preferences", access_level="member")
|
||||
def set_prefs(self, request, _, pref: str, value):
|
||||
account = self.account_service.get_account(request.sender.char_id)
|
||||
if not account:
|
||||
return "You dont have any preferences you could manage...."
|
||||
self.set_pref(request.sender.char_id, pref, 0 if value in ["off", "no"] else 1)
|
||||
return f"Your <highlight>{pref.capitalize()}</highlight> preference has been set to {value}."
|
||||
|
||||
@command(command="prefadmin",
|
||||
params=[Const("set"),
|
||||
Character("main"),
|
||||
Options(["news", "raidinvite", "subtilespam", "autoinvite", "raidspam"]),
|
||||
Options(["on", "off", "yes", "no"])],
|
||||
description="Change your Preferences",
|
||||
sub_command='mdf',
|
||||
access_level="admin")
|
||||
def set_pref_admin(self, _, _1, main, pref: str, value):
|
||||
self.set_pref(main.char_id, pref, 0 if value in ["off", "no"] else 1)
|
||||
return f"<highlight>{main.name}</highlight>'s <highlight>{pref.capitalize()}</highlight> " \
|
||||
f"preference has been set to {value}."
|
||||
|
||||
def get_prefs(self, char_id):
|
||||
return self.account_service.get_account(char_id)
|
||||
|
||||
def set_pref(self, char_id, pref, value):
|
||||
pref = "subtile_spam" if pref == "subtilespam" \
|
||||
else "raid_invite" if pref == "raidinvite" \
|
||||
else "news_spam" if pref == "news" \
|
||||
else "auto_invite" if pref == "autoinvite" \
|
||||
else 'raid_spam' if pref == "raidspam" \
|
||||
else ""
|
||||
|
||||
self.db.exec(f"UPDATE account set {pref}={value} where char_id=(SELECT main from account where char_id=?)",
|
||||
[char_id])
|
||||
|
||||
def get_pref_view_small(self, prefs):
|
||||
return f"\n" \
|
||||
f" └ [{self._make_cmd('news', prefs.news_spam)}] News - " \
|
||||
f"Autoinvite [{self._make_cmd('autoinvite', prefs.auto_invite)}], \n" \
|
||||
f" └ [{self._make_cmd('raidinvite', prefs.raid_invite)}] Raidinvite - " \
|
||||
f"Massmessage [{self._make_cmd('raidspam', prefs.raid_invite)}], \n" \
|
||||
f" └ [{self._make_cmd('subtilespam', prefs.subtile_spam)}] Subtilespam"
|
||||
|
||||
def get_pref_view_full(self, prefs):
|
||||
return f"\n" \
|
||||
f"<tab>└ [ {self._make_cmd('news', prefs.news_spam)} ] Do you want your News on Logon? \n" \
|
||||
f"<tab>└ [ {self._make_cmd('autoinvite', prefs.auto_invite)} ] Do you want to receive autoinvites? \n" \
|
||||
f"<tab>└ [ {self._make_cmd('raidinvite', prefs.raid_invite)} ] Do you want to receive raidinvites? \n" \
|
||||
f"<tab>└ [ {self._make_cmd('raidspam', prefs.raid_invite)} ] Do you want to receive massmessages? \n" \
|
||||
f"<tab>└ [ {self._make_cmd('subtilespam', prefs.subtile_spam)} ] Do you want a subtile invite spam? \n"
|
||||
|
||||
def _make_cmd(self, pref, value):
|
||||
# ---
|
||||
# [ ON | OFF ]
|
||||
if value == 1:
|
||||
return f"<green>YES</green> | {self.text.make_chatcmd('NO', f'/tell <myname> preferences set {pref} off')}"
|
||||
else:
|
||||
return f"{self.text.make_chatcmd('YES', f'/tell <myname> preferences set {pref} on')} | <red>NO</red>"
|
||||
@@ -0,0 +1,254 @@
|
||||
import time
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_alias_service import CommandAliasService
|
||||
from core.command_param_types import Const, Character, Any, Int
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.event_service import EventService
|
||||
from core.lookup.character_history_service import CharacterHistoryService
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.message_hub_service import MessageHubService
|
||||
from core.setting_service import SettingService
|
||||
from core.text import Text
|
||||
from core.tyrbot import Tyrbot
|
||||
from core.util import Util
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
|
||||
|
||||
@instance()
|
||||
class RegisterController:
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.buddy_service = registry.get_instance("buddy_service")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.event_service: EventService = registry.get_instance("event_service")
|
||||
self.pork_service: PorkService = registry.get_instance("pork_service")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.character_history_service: CharacterHistoryService = registry.get_instance("character_history_service")
|
||||
self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
|
||||
self.messagehub: MessageHubService = registry.get_instance("message_hub_service")
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("pending", "register pending")
|
||||
self.command_alias_service.add_alias("recommend", "register recommend")
|
||||
self.messagehub.register_message_source("registration")
|
||||
|
||||
@command(command="register", params=[Const("pending"), Int('count', is_optional=True)],
|
||||
description="Show the latest Recommendations", sub_command="mng", access_level="admin")
|
||||
def register_pending(self, _, _1, limit):
|
||||
limit = limit or 25
|
||||
latest = self.db.query("SELECT "
|
||||
"main.char_id as main_id, "
|
||||
"alt.char_id as alt_id, "
|
||||
"recommender.char_id as recommender_id, "
|
||||
|
||||
"main.name as main_name, "
|
||||
"alt.name as alt_name, "
|
||||
"recommender.name as recommender_name, "
|
||||
|
||||
"main.faction as main_faction, "
|
||||
"alt.faction as alt_faction, "
|
||||
"recommender.faction as recommender_faction, "
|
||||
|
||||
"main.level as main_level, "
|
||||
"alt.level as alt_level, "
|
||||
"recommender.level as main_level, "
|
||||
|
||||
"alt.ai_level as alt_ai_level, "
|
||||
"main.ai_level as main_ai_level, "
|
||||
"recommender.ai_level as recommender_ai_level, "
|
||||
|
||||
"reason "
|
||||
"from pending_accounts p "
|
||||
"left join player alt on alt.char_id = p.alt "
|
||||
"left join player main on main.char_id = p.main "
|
||||
"left join player recommender on recommender.char_id = p.recommender where answered=0 "
|
||||
"order by time desc limit ?", [limit])
|
||||
blob = ""
|
||||
for entry in latest:
|
||||
blob += self.format_pending(entry, buttons=True)
|
||||
|
||||
return ChatBlob("Latest recommendations", blob)
|
||||
|
||||
@command(command="register", params=[Const("pending"), Const('latest'), Int('count', is_optional=True)],
|
||||
description="Show the latest Recommendations", sub_command="mng", access_level="admin")
|
||||
def register_pending_latest(self, _, _1, _2, limit):
|
||||
limit = limit or 25
|
||||
latest = self.db.query("SELECT "
|
||||
"main.char_id as main_id, "
|
||||
"alt.char_id as alt_id, "
|
||||
"recommender.char_id as recommender_id, "
|
||||
|
||||
"main.name as main_name, "
|
||||
"alt.name as alt_name, "
|
||||
"recommender.name as recommender_name, "
|
||||
|
||||
"main.faction as main_faction, "
|
||||
"alt.faction as alt_faction, "
|
||||
"recommender.faction as recommender_faction, "
|
||||
|
||||
"main.level as main_level, "
|
||||
"alt.level as alt_level, "
|
||||
"recommender.level as main_level, "
|
||||
|
||||
"alt.ai_level as alt_ai_level, "
|
||||
"main.ai_level as main_ai_level, "
|
||||
"recommender.ai_level as recommender_ai_level, "
|
||||
|
||||
"reason "
|
||||
"from pending_accounts p "
|
||||
"left join player alt on alt.char_id = p.alt "
|
||||
"left join player main on main.char_id = p.main "
|
||||
"left join player recommender on recommender.char_id = p.recommender "
|
||||
"order by time desc limit ?", [limit])
|
||||
blob = ""
|
||||
for entry in latest:
|
||||
blob += self.format_pending(entry)
|
||||
|
||||
return ChatBlob("Latest recommendations", blob)
|
||||
|
||||
# noinspection LongLine
|
||||
def format_pending(self, entry, buttons=False):
|
||||
button = "[<green>" + self.text.make_tellcmd(name="APPROVE", msg=f"pending approve {entry.alt_name}",
|
||||
style="style='text-decoration:none'") + "</green>] - [<red>" + self.text.make_tellcmd(
|
||||
name="DENY", msg=f"pending deny {entry.alt_name}",
|
||||
style="style='text-decoration:none'") + "</red>]" if buttons else ""
|
||||
if entry.main_id == entry.alt_id:
|
||||
text = f"[<notice>ACC</notice>] <{entry.main_faction.lower()}>{entry.main_name}</{entry.main_faction.lower()}> ({entry.main_level}/<green>{entry.main_ai_level}</green>) " \
|
||||
f" [{self.text.make_tellcmd('W', f'whois {entry.main_name}')}] " \
|
||||
f"[{self.text.make_tellcmd('H', f'history {entry.main_name}')}]\n" \
|
||||
f"Recommender: <{entry.recommender_faction.lower()}>{entry.recommender_name}</{entry.recommender_faction.lower()}> Why: <notice>{entry.reason}</notice>\n" \
|
||||
f"{button}"
|
||||
else:
|
||||
account = self.account_service.get_account(entry.main_id)
|
||||
text = f"[<notice>ALT</notice>] <{entry.alt_faction.lower()}>{entry.alt_name}</{entry.alt_faction.lower()}> ({entry.alt_level}/<green>{entry.alt_ai_level}</green>) " \
|
||||
f" [{self.text.make_tellcmd('W', f'whois {entry.alt_name}')}] " \
|
||||
f"[{self.text.make_tellcmd('H', f'history {entry.alt_name}')}]\n" \
|
||||
f"Main: <{entry.main_faction.lower()}>{entry.main_name}</{entry.main_faction.lower()}> ({entry.main_level}/<green>{entry.main_ai_level}</green>) [Join: {self.util.format_datetime(account.created)}]" \
|
||||
f" - [{self.text.make_tellcmd('W', f'whois {entry.main_name}')}] " \
|
||||
f"[{self.text.make_tellcmd('H', f'history {entry.main_name}')}]\n" \
|
||||
f"{button}"
|
||||
|
||||
return text + "\n\n"
|
||||
|
||||
@command(command="register", params=[Const("pending"), Const('deny'), Character('character')],
|
||||
description="Deny (and disable) a pending account", sub_command="mng", access_level="admin")
|
||||
def register_pending_deny(self, request, _1, _2, user):
|
||||
pending = self.account_service.is_pending(user.char_id)
|
||||
if not pending:
|
||||
return f"There's no pending registration for <highlight>{user.name}</highlight>."
|
||||
self.account_service.create_users([(pending.alt, pending.alt, -1, time.time(), time.time())])
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Created <highlight>{user.name}</highlight>'s account.",
|
||||
request.sender.char_id)
|
||||
self.account_service.add_log(user.char_id, "system",
|
||||
f"Account closed by <highlight>{request.sender.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
|
||||
self.db.exec("UPDATE pending_accounts set answered = 1 where alt=?", [pending.alt])
|
||||
self.account_service.account_disable(user.char_id)
|
||||
self.send_notify(self.Notify.DENIED, user.name, user.name)
|
||||
|
||||
# noinspection LongLine
|
||||
@command(command="register", params=[Const("pending"), Const('approve'), Character('character')],
|
||||
description="Approve (and activate) a pending account", sub_command="mng", access_level="admin")
|
||||
def register_pending_approve(self, request, _1, _2, user):
|
||||
pending = self.account_service.is_pending(user.char_id)
|
||||
if not pending:
|
||||
return f"There's no pending registration for <highlight>{user.name}</highlight>."
|
||||
self.account_service.create_users([(pending.alt, pending.alt, 0, time.time(), time.time())])
|
||||
if pending.alt == pending.main:
|
||||
self.db.exec("UPDATE pending_accounts set answered = 1 where alt=?", [pending.alt])
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Opened <highlight>{user.name}</highlight>'s account.",
|
||||
request.sender.char_id)
|
||||
self.account_service.add_log(user.char_id, "system",
|
||||
f"Account opened by <highlight>{request.sender.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
recommender: str = self.character_service.get_char_name(pending.recommender)
|
||||
self.buddy_service.add_buddy(user.char_id, "member")
|
||||
self.bot.send_mass_message(user.char_id,
|
||||
f"Your Account has been <green>opened</green> by "
|
||||
f"<highlight>{request.sender.name}</highlight>. "
|
||||
f"<highlight>{recommender.capitalize()}</highlight> has recommended you.")
|
||||
self.send_notify(self.Notify.APPROVAL_MAIN, user.name, user.name)
|
||||
else:
|
||||
msg, typ = self.account_service.add_alt(pending.main, pending.alt, approve=True)
|
||||
if msg == "success":
|
||||
self.db.exec("UPDATE pending_accounts set answered = 1 where alt=?", [pending.alt])
|
||||
self.account_service.add_log(request.sender.char_id, "system",
|
||||
f"Added <highlight>{user.name}</highlight> as an alt of <highlight>{self.character_service.resolve_char_to_name(pending.main)}</highlight>.",
|
||||
request.sender.char_id)
|
||||
self.account_service.add_log(user.char_id, "system",
|
||||
f"Request to add <highlight>{user.name}</highlight> as an alt "
|
||||
f"has been approved by <highlight>{request.sender.name}</highlight>.",
|
||||
request.sender.char_id)
|
||||
self.send_notify(self.Notify.APPROVAL_ALT,
|
||||
self.character_service.resolve_char_to_name(pending.main),
|
||||
user.name)
|
||||
else:
|
||||
return "Something went wrong.."
|
||||
|
||||
@command(command="register", params=[Const('alt'), Character('character')],
|
||||
description="Add an alt", access_level="member")
|
||||
def register_alt(self, request, _, alt):
|
||||
manual = self.setting_service.get_value("alt_verification") == "1"
|
||||
if not alt.char_id:
|
||||
return "Character does not exist"
|
||||
if not manual:
|
||||
return "This command is currently disabled, please use <highlight>!alts add</highlight>"
|
||||
msg, _ = self.account_service.add_pending_alt(request.sender.char_id, alt.char_id)
|
||||
if msg in ["another_main", "already_main"]:
|
||||
return f"You cannot add <highlight>{alt.name}</highlight> as an alt, because it already has a main."
|
||||
elif msg == "pending_alt":
|
||||
return f"There's already a pending registration for the alt <highlight>{alt.name}</highlight>, " \
|
||||
f"please wait for an admin to confirm it."
|
||||
elif msg == "success":
|
||||
self.send_notify(self.Notify.REQUEST_ALT, request.sender.name, alt.name)
|
||||
return f"You requested to add <highlight>{alt.name}</highlight> as an alt."
|
||||
|
||||
@command(command="register", params=[Const('recommend'), Character('character'), Any('reason')],
|
||||
description="Recommend someone as a raider", access_level="member")
|
||||
def register_account(self, request, _, user, reason):
|
||||
if not user.char_id:
|
||||
return "Character does not exist"
|
||||
if self.account_service.get_account(user.char_id):
|
||||
return f"There's already an account for <highlight>{user.name}</highlight> registered."
|
||||
msg, _ = self.account_service.add_pending_account(request.sender.char_id, user.char_id, reason)
|
||||
if msg in ["another_main", "already_main"]:
|
||||
return f"You cannot recommend <highlight>{user.name}</highlight>, " \
|
||||
f"because the character is already registered."
|
||||
elif msg == "pending_alt":
|
||||
return f"There's already a pending registration for the character <highlight>{user.name}</highlight>, " \
|
||||
f"please wait for an admin to confirm it."
|
||||
elif msg == "success":
|
||||
self.send_notify(self.Notify.REQUEST_MAIN, user.name, user.name)
|
||||
|
||||
def send_notify(self, notify_type, main, alt):
|
||||
message = "[<cyan>PG</cyan>] "
|
||||
if notify_type == self.Notify.APPROVAL_MAIN:
|
||||
message += f"[<cyan>Main</cyan>] Approved: <highlight>{main}</highlight>'s Account has been opened."
|
||||
elif notify_type == self.Notify.APPROVAL_ALT:
|
||||
message += f"[<cyan>Alt</cyan>] Approved: <highlight>{alt}</highlight> " \
|
||||
f"has been assigned to <highlight>{main}</highlight>."
|
||||
elif notify_type == self.Notify.DENIED:
|
||||
message += f"Denied: <highlight>{main}</highlight>'s Account has been closed."
|
||||
elif notify_type == self.Notify.REQUEST_ALT:
|
||||
message += f"[<cyan>Alt</cyan>] Request: <highlight>{alt}</highlight> => <highlight>{main}</highlight>"
|
||||
elif notify_type == self.Notify.REQUEST_MAIN:
|
||||
message += f"[<cyan>Main</cyan>] Request: <highlight>{main}</highlight> as a new " \
|
||||
f"<highlight>raider</highlight>"
|
||||
self.messagehub.send_message("registration", None, f"{message}", f"{message}")
|
||||
|
||||
class Notify:
|
||||
APPROVAL_MAIN = "approval-main"
|
||||
APPROVAL_ALT = "approval-alt"
|
||||
DENIED = "denied"
|
||||
REQUEST_ALT = "req-alt"
|
||||
REQUEST_MAIN = "req-main"
|
||||
@@ -0,0 +1,137 @@
|
||||
import inspect
|
||||
from typing import List
|
||||
|
||||
from core.decorators import instance
|
||||
from core.logger import Logger
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@instance()
|
||||
class AccessService:
|
||||
def __init__(self):
|
||||
self.access_levels = [
|
||||
{"label": "none", "level": 0, "handler": self.no_access},
|
||||
{"label": "all", "level": 100, "handler": self.all_access}]
|
||||
self.logger = Logger(__name__)
|
||||
|
||||
def inject(self, registry):
|
||||
self.character_service = registry.get_instance("character_service")
|
||||
self.account_service = registry.get_instance("account_service")
|
||||
|
||||
def register_access_level(self, label, level, handler):
|
||||
"""
|
||||
Call during pre_start
|
||||
|
||||
Args:
|
||||
label: str
|
||||
level: int
|
||||
handler: (char_id: Int) -> bool
|
||||
"""
|
||||
|
||||
if len(inspect.signature(handler).parameters) != 1:
|
||||
raise Exception(
|
||||
"Incorrect number of arguments for handler '%s.%s()'" % (handler.__module__, handler.__name__))
|
||||
|
||||
self.logger.debug("Registering access level %d with label '%s'" % (level, label))
|
||||
self.access_levels.append({"label": label.lower(), "level": level, "handler": handler})
|
||||
self.access_levels = sorted(self.access_levels, key=lambda k: k["level"])
|
||||
|
||||
def get_access_levels(self) -> List[dict]:
|
||||
return self.access_levels
|
||||
|
||||
def get_access_level(self, char_id) -> dict:
|
||||
account = self.account_service.get_main(char_id)
|
||||
if account:
|
||||
al = self.get_single_access_level(account.char_id)
|
||||
if al["label"] == "all":
|
||||
al = self.get_single_access_level(char_id)
|
||||
return al
|
||||
else:
|
||||
return self.get_single_access_level(char_id)
|
||||
# access_level1 = self.get_single_access_level(char_id)
|
||||
#
|
||||
# alts = self.account_service.get_alts(char_id)
|
||||
# if not alts:
|
||||
# return access_level1
|
||||
#
|
||||
# main = alts[0]
|
||||
# if main.char_id == char_id:
|
||||
# return access_level1
|
||||
# else:
|
||||
# access_level2 = self.get_single_access_level(main.char_id)
|
||||
# if access_level1["level"] < access_level2["level"]:
|
||||
# return access_level1
|
||||
# else:
|
||||
# return access_level2
|
||||
|
||||
def compare_access_levels(self, access_level1, access_level2) -> int:
|
||||
"""
|
||||
Returns a positive number if the access_level1 is greater than access_level2,
|
||||
a negative number if access_level1 is less than access_level2,
|
||||
and 0 if the access levels are equal.
|
||||
|
||||
:param access_level1:
|
||||
:param access_level2:
|
||||
:return: int
|
||||
"""
|
||||
a1 = self.get_access_level_by_label(access_level1)
|
||||
a2 = self.get_access_level_by_label(access_level2)
|
||||
|
||||
return a2["level"] - a1["level"]
|
||||
|
||||
def has_sufficient_access_level(self, char_id1, char_id2) -> bool:
|
||||
"""
|
||||
Returns True if char1 has a higher access level than char2
|
||||
or if char1 is a verified alt of char2, and False otherwise.
|
||||
|
||||
:param char_id1:
|
||||
:param char_id2:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# return True if char_ids are the same
|
||||
if char_id1 == char_id2:
|
||||
return True
|
||||
|
||||
# return True if both chars have the same main
|
||||
if self.account_service.get_main(char_id1).char_id == (self.account_service.get_main(char_id2) or {}).get(
|
||||
"char_id", 0):
|
||||
return True
|
||||
|
||||
a1 = self.get_access_level(char_id1)
|
||||
a2 = self.get_access_level(char_id2)
|
||||
|
||||
return a2["level"] - a1["level"] > 0
|
||||
|
||||
def get_single_access_level(self, char) -> dict:
|
||||
char_id = self.character_service.resolve_char_to_id(char)
|
||||
for access_level in self.access_levels:
|
||||
if access_level["handler"](char_id):
|
||||
return access_level
|
||||
|
||||
def get_access_level_by_level(self, level) -> dict or bool:
|
||||
for access_level in self.access_levels:
|
||||
if access_level["level"] == level:
|
||||
return access_level
|
||||
return False
|
||||
|
||||
def get_access_level_by_label(self, label) -> dict or bool:
|
||||
label = label.lower()
|
||||
for access_level in self.access_levels:
|
||||
if access_level["label"] == label:
|
||||
return access_level
|
||||
return False
|
||||
|
||||
def check_access(self, char, access_level_label) -> bool:
|
||||
char_id = self.character_service.resolve_char_to_id(char)
|
||||
if not char_id:
|
||||
return False
|
||||
# noinspection LongLine
|
||||
return (self.get_access_level(char) or {}).get("level", 100) <= \
|
||||
self.get_access_level_by_label(access_level_label)["level"]
|
||||
|
||||
def no_access(self, char_id) -> bool:
|
||||
return False
|
||||
|
||||
def all_access(self, char_id) -> bool:
|
||||
return True
|
||||
@@ -0,0 +1,568 @@
|
||||
import time
|
||||
from typing import List
|
||||
|
||||
from core.buddy_service import BuddyService
|
||||
from core.db import DB, SqlException
|
||||
from core.decorators import instance, timerevent, event
|
||||
from core.dict_object import DictObject
|
||||
from core.logger import Logger
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.setting_service import SettingService
|
||||
from core.setting_types import BooleanSettingType
|
||||
from core.text import Text
|
||||
from core.tyrbot import Tyrbot
|
||||
from modules.core.accounting.services.access_service import AccessService
|
||||
|
||||
|
||||
# noinspection SqlCaseVsIf,SqlResolve,PyMethodMayBeStatic
|
||||
@instance()
|
||||
class AccountService:
|
||||
MAIN_CHANGED_EVENT_TYPE = "main_changed"
|
||||
MEMBER_LOGON = "member_logon"
|
||||
MEMBER_LOGOFF = "member_logoff"
|
||||
|
||||
def __init__(self):
|
||||
self.logger = Logger("Accounting")
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
|
||||
self.util = registry.get_instance("util")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.access_service: AccessService = registry.get_instance("access_service")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.event_service = registry.get_instance("event_service")
|
||||
self.pork: PorkService = registry.get_instance("pork_service")
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
|
||||
def pre_start(self):
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS account ("
|
||||
"char_id int(11) NOT NULL,"
|
||||
"points int(11) NOT NULL default 0,"
|
||||
"member int(2) NOT NULL DEFAULT 0,"
|
||||
"created int(255) NOT NULL DEFAULT 0,"
|
||||
"disabled int(2) NOT NULL DEFAULT 0,"
|
||||
"main int(11) NOT NULL DEFAULT char_id,"
|
||||
"subtile_spam int(2) NOT NULL DEFAULT 0,"
|
||||
"auto_invite int(2) NOT NULL DEFAULT 0,"
|
||||
"raid_invite int(2) NOT NULL DEFAULT 1,"
|
||||
"raid_spam int(2) NOT NULL DEFAULT 1,"
|
||||
"news_spam int(2) NOT NULL DEFAULT 1,"
|
||||
"discord_id bigint(30) UNSIGNED NULL DEFAULT 0,"
|
||||
"discord_handle varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',"
|
||||
"discord_invite text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',"
|
||||
"discord_joined int(2) NOT NULL DEFAULT 0,"
|
||||
"logon text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',"
|
||||
"logoff text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',"
|
||||
"last_seen int(11) NOT NULL DEFAULT 0,"
|
||||
"last_updated int(11) NOT NULL DEFAULT 0,"
|
||||
"PRIMARY KEY (char_id) USING BTREE,"
|
||||
"INDEX char_id(char_id) USING BTREE,"
|
||||
"INDEX main(main) USING BTREE,"
|
||||
"INDEX disabled(disabled) USING BTREE,"
|
||||
"INDEX char_id_2(char_id, disabled) USING BTREE,"
|
||||
"INDEX discord_id(discord_id) USING BTREE)")
|
||||
|
||||
self.db.exec(
|
||||
"CREATE TABLE IF NOT EXISTS raid_log ("
|
||||
"raid_id INT PRIMARY KEY AUTO_INCREMENT, "
|
||||
"raid_name VARCHAR(255) NOT NULL, "
|
||||
"started_by BIGINT NOT NULL, "
|
||||
"raid_start INT NOT NULL, "
|
||||
"raid_end INT NOT NULL)")
|
||||
|
||||
self.db.exec(
|
||||
"CREATE TABLE IF NOT EXISTS raid_log_participants ("
|
||||
"raid_id INT NOT NULL, "
|
||||
"raider_id BIGINT NOT NULL, "
|
||||
"accumulated_points INT DEFAULT 0, "
|
||||
"left_raid INT, "
|
||||
"was_kicked INT, "
|
||||
"was_kicked_reason VARCHAR(500))")
|
||||
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS points_presets ("
|
||||
"preset_id INT PRIMARY KEY AUTO_INCREMENT, "
|
||||
"name VARCHAR(50) NOT NULL, "
|
||||
"points INT DEFAULT 1, "
|
||||
"UNIQUE(name));")
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS pending_accounts ("
|
||||
"main INT(11) NOT NULL, "
|
||||
"alt INT(11) NOT NULL, "
|
||||
"reason TEXT DEFAULT '', "
|
||||
"recommender INT(11) NOT NULL , "
|
||||
"time int(255) NOT NULL,"
|
||||
"answered int(2) default 0 not null);")
|
||||
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS account_log ("
|
||||
"log_id INTEGER PRIMARY KEY AUTO_INCREMENT, "
|
||||
"char_id BIGINT NOT NULL, "
|
||||
"type VARCHAR(32), "
|
||||
"delta INT NOT NULL DEFAULT 0, "
|
||||
"leader_id BIGINT NOT NULL, "
|
||||
"reason VARCHAR(255), "
|
||||
"created_at INTEGER NOT NULL, "
|
||||
"INDEX char_id (char_id), "
|
||||
"INDEX leader (leader_id), "
|
||||
"INDEX created(created_at));")
|
||||
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS ranks ("
|
||||
"main int(11) NOT NULL, "
|
||||
"`rank` varchar(32) not null, "
|
||||
"INDEX main(main, `rank`) USING BTREE)")
|
||||
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS org_bots(char_id int primary key not null, org_id int not null)")
|
||||
|
||||
self.event_service.register_event_type(self.MAIN_CHANGED_EVENT_TYPE)
|
||||
self.event_service.register_event_type(self.MEMBER_LOGON)
|
||||
self.event_service.register_event_type(self.MEMBER_LOGOFF)
|
||||
self.setting_service.register_new(self.module_name, "is_alliance_bot", False, BooleanSettingType(),
|
||||
"Is this bot used as an alliancebot")
|
||||
self.setting_service.register_new(self.module_name, "alt_verification", False, BooleanSettingType(),
|
||||
"alts require admin verification")
|
||||
|
||||
if self.setting_service.get_value("is_alliance_bot") == "1":
|
||||
self.access_service.register_access_level("officer", 80, self.check_officer)
|
||||
self.access_service.register_access_level("general", 70, self.check_general)
|
||||
self.access_service.register_access_level("president", 60, self.check_president)
|
||||
self.access_service.register_access_level("council", 40, self.check_council)
|
||||
self.access_service.register_access_level("moderator", 30, self.check_moderator)
|
||||
self.access_service.register_access_level("member", 90, self.check_member)
|
||||
self.access_service.register_access_level("admin", 20, self.check_admin)
|
||||
self.access_service.register_access_level("leader", 50, self.check_leader)
|
||||
self.access_service.register_access_level("superadmin", 10, self.check_superadmin)
|
||||
|
||||
def get_main(self, char_id) -> DictObject:
|
||||
alts = self.get_alts(char_id)
|
||||
return alts[0] if alts else self.db.query_single("select * from player where char_id=?", [char_id])
|
||||
|
||||
def get_alts(self, char_id) -> List[DictObject]:
|
||||
return self.db.query(
|
||||
"SELECT p.*, a.* from account a left join player p on a.char_id = p.char_id where "
|
||||
"main=(SELECT main from account where char_id=?) ORDER BY a.main = a.char_id desc, p.level, p.name DESC",
|
||||
[char_id])
|
||||
|
||||
acc_cache = {}
|
||||
|
||||
def get_account(self, char_id) -> DictObject:
|
||||
|
||||
if char_id not in self.acc_cache.keys():
|
||||
out = self.db.query_single(
|
||||
"SELECT a.*, p.* from account a left join player p on a.char_id = p.char_id where "
|
||||
"a.char_id=(SELECT main from account where char_id=?) "
|
||||
"and a.char_id not in (SELECT char_id from org_bots)",
|
||||
[char_id]) or DictObject({})
|
||||
self.acc_cache[char_id] = out
|
||||
self.bot.job_scheduler.delayed_job(lambda x: self.acc_cache.pop(char_id), 5)
|
||||
else:
|
||||
out = self.acc_cache.get(char_id)
|
||||
return out
|
||||
|
||||
def get_entry(self, char_id) -> DictObject:
|
||||
return self.db.query_single("SELECT a.*, p.* from account a left join player p on a.char_id = p.char_id "
|
||||
"where a.char_id=? and a.char_id not in (SELECT char_id from org_bots)",
|
||||
[char_id]) or DictObject({})
|
||||
|
||||
def add_pending_alt(self, main, alt) -> [str, bool]:
|
||||
data = self.check_alt(alt)
|
||||
if data:
|
||||
return data
|
||||
acc = self.get_account(main)
|
||||
if acc:
|
||||
main = acc.main
|
||||
self.pork.load_character_info(alt, skeleton_only=True)
|
||||
self.db.exec("INSERT INTO pending_accounts(main, alt, recommender, time) VALUES(?, ?, ?, ?)",
|
||||
[acc.main, alt, acc.main, time.time()])
|
||||
self.add_log(main, 'system',
|
||||
f'Requested to add <highlight>{self.character_service.resolve_char_to_name(alt)}</highlight>'
|
||||
f' as an alt',
|
||||
acc.main)
|
||||
return ["success", True]
|
||||
|
||||
def add_pending_account(self, main, user, reason) -> [str, bool]:
|
||||
data = self.check_alt(user)
|
||||
if data:
|
||||
return data
|
||||
acc = self.get_account(main)
|
||||
if acc:
|
||||
main = acc.main
|
||||
self.pork.load_character_info(user, skeleton_only=True)
|
||||
self.db.exec("INSERT INTO pending_accounts(main, alt, recommender, reason, time) VALUES(?, ?, ?, ?, ?)",
|
||||
[user, user, acc.main, reason, time.time()])
|
||||
self.add_log(main, 'system',
|
||||
f'Recommended <highlight>{self.character_service.resolve_char_to_name(user)}</highlight>'
|
||||
f' as raider, reason: {reason}',
|
||||
acc.main)
|
||||
return ["success", True]
|
||||
|
||||
def check_alt(self, alt):
|
||||
alt_alts = self.get_alts(alt) or []
|
||||
if len(alt_alts) > 1:
|
||||
for alts in alt_alts:
|
||||
if alts.char_id == alt:
|
||||
if alts.main != alts.char_id:
|
||||
return ["another_main", False]
|
||||
alt_main = self.get_account(alt)
|
||||
if alt_main:
|
||||
if alt_main.main != alt:
|
||||
return ["already_main", False]
|
||||
if self.is_pending(alt):
|
||||
return ['pending_alt', False]
|
||||
|
||||
def is_pending(self, char_id) -> DictObject or bool:
|
||||
return self.db.query_single("SELECT * from pending_accounts where alt=? and answered = 0", [char_id]) or False
|
||||
|
||||
def add_alt(self, sender, alt, approve=False) -> [str, bool]:
|
||||
alt_alts = self.get_alts(alt) or []
|
||||
if alt_alts:
|
||||
if len(alt_alts) > 1:
|
||||
return ["another_main", False]
|
||||
if alt_main := alt_alts[0]:
|
||||
if alt_main.main != alt:
|
||||
return ["already_main", False]
|
||||
acc = self.get_account(sender)
|
||||
if acc:
|
||||
main = acc.main
|
||||
self.pork.load_character_info(alt, skeleton_only=True)
|
||||
member = -1 if acc.disabled == 1 or acc.member == -1 else 0
|
||||
if self.db.exec("INSERT IGNORE INTO account(char_id, main, created, member) VALUES(?, ?, ?, ?)",
|
||||
[alt, main, time.time(), member]) == 0:
|
||||
self.db.exec("UPDATE account set main=?, created=? where char_id=?", [main, time.time(), alt])
|
||||
self.event_service.fire_event(self.MAIN_CHANGED_EVENT_TYPE,
|
||||
DictObject({"old_main_id": alt, "new_main_id": main}))
|
||||
if not approve:
|
||||
self.add_log(sender, 'system',
|
||||
f'Added <highlight>{self.character_service.resolve_char_to_name(alt)}</highlight>'
|
||||
f' as an alt',
|
||||
acc.main)
|
||||
# Only add as member if he's neither banned, nor unregistered
|
||||
if member != -1:
|
||||
self.buddy_service.add_buddy(alt, "member")
|
||||
return ["success", True]
|
||||
self.db.exec("INSERT IGNORE INTO account(char_id, main, created, member) VALUES(?, ?, ?, ?)",
|
||||
[sender, sender, time.time(), -1])
|
||||
self.db.exec("INSERT INTO account(char_id, main, created, member) VALUES(?, ?, ?, ?) ON DUPLICATE KEY UPDATE "
|
||||
"main=VALUE(main), discord_id=0, discord_handle='', discord_invite='', discord_joined=0",
|
||||
[alt, sender, time.time(), -1])
|
||||
if not approve:
|
||||
self.add_log(sender, 'system',
|
||||
f'Added <highlight>{self.character_service.resolve_char_to_name(alt)}</highlight> as an alt',
|
||||
sender)
|
||||
# Main does not exist, do not add as member
|
||||
# self.buddy_service.add_buddy(alt, "member")
|
||||
return ["success", True]
|
||||
|
||||
def remove_alt(self, sender, alt) -> [str, bool]:
|
||||
if not self.db.query(
|
||||
"SELECT * FROM account where char_id=? and main=(SELECT main from account where char_id=?)",
|
||||
[alt, sender]):
|
||||
return ["not_alt", False]
|
||||
alts = self.get_alts(sender)
|
||||
for row in alts:
|
||||
if row.char_id == alt and row.main == alt:
|
||||
return ["remove_main", False]
|
||||
if row.char_id == alt and row.main == alts[0].char_id:
|
||||
self.db.exec("UPDATE account set main=? where char_id=?", [alt, alt])
|
||||
self.add_log(sender, 'system',
|
||||
f'Removed <highlight>{self.character_service.resolve_char_to_name(alt)}</highlight>'
|
||||
f' from his alts.',
|
||||
sender)
|
||||
return ["success", True]
|
||||
else:
|
||||
return [""]
|
||||
|
||||
def set_as_main(self, sender) -> [str, bool]:
|
||||
alts = self.get_alts(sender)
|
||||
|
||||
if len(alts) < 2:
|
||||
return ["not_an_alt", False]
|
||||
elif alts[0].char_id == sender:
|
||||
return ["already_main", False]
|
||||
else:
|
||||
self.db.exec("UPDATE account set main=? where main=(SELECT main from account where char_id=?)",
|
||||
[sender, sender])
|
||||
self.event_service.fire_event(self.MAIN_CHANGED_EVENT_TYPE,
|
||||
DictObject({"old_main_id": alts[0].char_id,
|
||||
"new_main_id": sender}))
|
||||
return ["success", True]
|
||||
|
||||
def get_orgs(self) -> list:
|
||||
try:
|
||||
return [x["org_id"] for x in self.db.query("SELECT * from orgs", [])]
|
||||
except SqlException:
|
||||
return [self.bot.public_channel_service.org_id]
|
||||
|
||||
def create_users(self, users, disable=False) -> int:
|
||||
if type(users) == list and len(users) > 0:
|
||||
with self.db.pool.get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
if disable:
|
||||
cur.executemany(
|
||||
"INSERT IGNORE INTO account(char_id, main, member, disabled, last_updated, created) "
|
||||
"VALUES(?, ?, ?, 1, ?, ?) ON DUPLICATE KEY UPDATE "
|
||||
"member=VALUE(member), last_updated=VALUE(last_updated), disabled=1",
|
||||
users)
|
||||
return cur.rowcount
|
||||
cur.executemany(
|
||||
"INSERT IGNORE INTO account(char_id, main, member, last_updated, created) "
|
||||
"VALUES(?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE "
|
||||
"member=VALUE(member), last_updated=VALUE(last_updated)",
|
||||
users)
|
||||
return cur.rowcount
|
||||
|
||||
def account_disable(self, char_id) -> bool:
|
||||
return True if self.db.exec(
|
||||
"UPDATE account set disabled=1 where main in (SELECT main from account where char_id=?)",
|
||||
[char_id]) else False
|
||||
|
||||
def account_enable(self, char_id) -> bool:
|
||||
return True if self.db.exec(
|
||||
"UPDATE account set disabled=0 where main in (SELECT main from account where char_id=?)",
|
||||
[char_id]) else False
|
||||
|
||||
def remove_members(self, users) -> None:
|
||||
if type(users) == list and len(users) > 0:
|
||||
with self.db.pool.get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.executemany("UPDATE account set member=-1 where char_id=? and ?", users)
|
||||
|
||||
def check_member(self, char_id) -> bool:
|
||||
account = self.get_account(char_id) or {}
|
||||
if account.get('disabled', 1) == 1:
|
||||
return False
|
||||
if account.get('member', -1) == -1:
|
||||
return False
|
||||
if self.setting_service.get_value("is_alliance_bot") == "0":
|
||||
return True
|
||||
if account.get('org_id', 0) not in self.get_orgs():
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_president(self, char_id) -> bool:
|
||||
if self.setting_service.get_value("is_alliance_bot") == "0":
|
||||
return False
|
||||
account = self.get_account(char_id) or {}
|
||||
if not self.simple_checks(account):
|
||||
return False
|
||||
if account.get('org_rank_id', -1) == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_general(self, char_id) -> bool:
|
||||
if self.setting_service.get_value("is_alliance_bot") == "0":
|
||||
return False
|
||||
account = self.get_account(char_id) or {}
|
||||
if not self.simple_checks(account):
|
||||
return False
|
||||
if self.get_rank_count(char_id) != 7:
|
||||
return False
|
||||
if account.get('org_rank_id', -1) <= 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_officer(self, char_id) -> bool:
|
||||
if self.setting_service.get_value("is_alliance_bot") == "0":
|
||||
return False
|
||||
account = self.get_account(char_id) or {}
|
||||
if not self.simple_checks(account):
|
||||
return False
|
||||
if self.get_rank_count(char_id) != 7:
|
||||
return False
|
||||
if account.get('org_rank_id', -1) <= 2:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_council(self, char_id) -> bool:
|
||||
if self.setting_service.get_value("is_alliance_bot") == "0":
|
||||
return False
|
||||
if self.simple_checks(self.get_account(char_id)):
|
||||
return "council" in self.get_ranks(char_id)
|
||||
|
||||
def check_leader(self, char_id) -> bool:
|
||||
if self.simple_checks(self.get_account(char_id)):
|
||||
return "leader" in self.get_ranks(char_id)
|
||||
|
||||
def check_moderator(self, char_id) -> bool:
|
||||
if self.simple_checks(self.get_account(char_id)):
|
||||
return "moderator" in self.get_ranks(char_id)
|
||||
|
||||
def check_admin(self, char_id) -> bool:
|
||||
if self.simple_checks(self.get_account(char_id)):
|
||||
return "admin" in self.get_ranks(char_id)
|
||||
|
||||
def check_superadmin(self, char_id) -> int:
|
||||
return char_id in self.bot.superadmin
|
||||
|
||||
def get_ranks(self, char_id) -> List[DictObject]:
|
||||
return [x["rank"] for x in
|
||||
self.db.query(" SELECT rank FROM ranks where main = (SELECT main from account where char_id=? limit 1)",
|
||||
[char_id])] or []
|
||||
|
||||
def get_all_admins(self) -> List[DictObject]:
|
||||
return self.get_by_group("admin")
|
||||
|
||||
def get_all_moderators(self) -> List[DictObject]:
|
||||
return self.get_by_group("moderator")
|
||||
|
||||
def get_all_councils(self) -> List[DictObject]:
|
||||
return self.get_by_group("council")
|
||||
|
||||
def get_all_leaders(self) -> List[DictObject]:
|
||||
return self.get_by_group("leader")
|
||||
|
||||
def get_all_members(self, online_only=False) -> List[DictObject]:
|
||||
return self.db.query(
|
||||
f"SELECT p.*, a.*, CASE when o.char_id IS NOT NULL then 1 ELSE 0 end as online from account a "
|
||||
f"LEFT JOIN player p ON a.char_id=p.char_id "
|
||||
f"LEFT JOIN (SELECT * FROM online WHERE bot=?) o ON a.char_id=o.char_id "
|
||||
f"WHERE a.char_id NOT IN (SELECT char_id from org_bots) "
|
||||
f"and a.disabled = 0 {'and o.char_id is not null' if online_only else ''} "
|
||||
f"order by a.main, a.main=a.char_id desc, p.level desc, p.ai_level desc",
|
||||
[self.bot.get_char_id()])
|
||||
|
||||
def get_by_group(self, group) -> List[DictObject]:
|
||||
return self.db.query("""SELECT CASE when rank = 'admin' then 0
|
||||
when rank = 'moderator' then 1
|
||||
when rank = 'council' then 2
|
||||
when rank = 'leader' then 3
|
||||
ELSE 99 END AS rank_id,
|
||||
CASE when o.char_id IS NOT NULL
|
||||
then 1
|
||||
ELSE 0 end as online,
|
||||
p.*, a.* FROM ranks r
|
||||
LEFT JOIN account a ON r.main=a.main
|
||||
LEFT JOIN player p ON a.char_id=p.char_id
|
||||
LEFT JOIN online o ON a.char_id=o.char_id
|
||||
WHERE r.rank=?
|
||||
ORDER BY rank_id, a.main desc, a.main=a.char_id DESC, p.name """, [group])
|
||||
|
||||
def get_group_tag(self, string) -> str or bool:
|
||||
string = string.lower()
|
||||
if string in ["adm", "admins", "admin", "administrator", "administrators"]:
|
||||
return "admin"
|
||||
elif string in ["mod", "mods", "moderator", "moderators"]:
|
||||
return "moderator"
|
||||
elif string in ["cnc", "council", "councilors"]:
|
||||
return "council"
|
||||
elif string in ["rl", "leader", "leaders", "raidleader", "raidleaders", "rls"]:
|
||||
return "leader"
|
||||
elif string in ["all", "full", "everyone"]:
|
||||
return "all"
|
||||
else:
|
||||
return False
|
||||
|
||||
def add_log(self, char_id, log_type, message, leader, delta=0) -> None:
|
||||
main = self.get_main(char_id).char_id
|
||||
self.db.exec("INSERT INTO account_log(char_id, type, delta, leader_id, reason, created_at) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
[main, log_type, delta, leader, message, time.time()])
|
||||
|
||||
def get_logs(self, user, log_type=None, limit=25) -> List[DictObject]:
|
||||
if not log_type:
|
||||
return self.db.query("SELECT * FROM account_log "
|
||||
"where char_id=? and type != 'admin' order by log_id desc LIMIT ? ", [user, limit])
|
||||
|
||||
else:
|
||||
return self.db.query("SELECT * FROM account_log "
|
||||
"where char_id=? and type=? order by log_id desc LIMIT ?", [user, log_type, limit])
|
||||
|
||||
def get_log_by_id(self, log_id) -> DictObject:
|
||||
return self.db.query_single("SELECT * FROM account_log where log_id=? ", [log_id])
|
||||
|
||||
def format_entry(self, entry) -> str:
|
||||
msg = f"<grey>[{self.util.format_datetime(entry.created_at)}] " \
|
||||
f"[{self.text.make_tellcmd('D', f'account log id {entry.log_id}')}]</grey> "
|
||||
entry.type = entry.type.lower()
|
||||
if entry.type == "points":
|
||||
entry.reason = entry.reason.replace('"', "'")
|
||||
msg += f"<white>{f'<green>+{entry.delta}P</green>' if entry.delta >= 0 else f'<red>{entry.delta}P</red>'}" \
|
||||
f" by <notice>{self.character_service.resolve_char_to_name(entry.leader_id)}</notice>" \
|
||||
f" for <notice>{entry.reason}</notice></white>"
|
||||
elif entry.type == "loot":
|
||||
msg += f"<white>Won Item: {entry.reason}</white>"
|
||||
elif entry.type == "raid":
|
||||
msg += f"<white>{entry.reason}</white>"
|
||||
elif entry.type == "public":
|
||||
msg += f"<white>{entry.reason}</white>"
|
||||
elif entry.type == "admin":
|
||||
msg += f"<white>Notice from " \
|
||||
f"<notice>{self.character_service.resolve_char_to_name(entry.leader_id)}</notice>: " \
|
||||
f"{entry.reason}</white>"
|
||||
elif entry.type == "system":
|
||||
msg += f"<yellow>{entry.reason}</yellow>"
|
||||
|
||||
return msg + "\n"
|
||||
|
||||
def add_pts(self, char_id, points, reason, leader) -> None:
|
||||
self.db.exec("UPDATE account set points = points+? where char_id=(select main from account where char_id=?)",
|
||||
[points, char_id])
|
||||
self.add_log(char_id, 'points', reason, leader, delta=points)
|
||||
|
||||
def rem_pts(self, char_id, points, reason, leader) -> None:
|
||||
if points < 0:
|
||||
points = -points
|
||||
self.db.exec("UPDATE account set points = points-? where char_id=(select main from account where char_id=?)",
|
||||
[points, char_id])
|
||||
self.add_log(char_id, 'points', reason, leader, delta=-points)
|
||||
|
||||
def add_rank(self, char_id, rank) -> int:
|
||||
return self.db.exec("INSERT INTO ranks(main, `rank`) VALUES(?, ?)", [char_id, rank])
|
||||
|
||||
def del_rank(self, char_id, rank) -> int:
|
||||
return self.db.exec("DELETE FROM ranks where main=? and rank =?", [char_id, rank])
|
||||
|
||||
@timerevent(budatime="12h", description="Delete dead ranks")
|
||||
def clear_raid(self, _, _1):
|
||||
count = self.db.exec("DELETE FROM ranks where main not in (SELECT main from account)")
|
||||
if count > 0:
|
||||
self.logger.info(f"Purged {count} dead ranks")
|
||||
count = self.db.exec(
|
||||
"delete from ranks where main in(select r.main from ranks r "
|
||||
"left join account a on r.main=a.char_id "
|
||||
"where a.member=-1)")
|
||||
if count > 0:
|
||||
self.logger.info(f"Purged {count} ranks; caused by: main no longer in the alliance")
|
||||
|
||||
@event(event_type="main_changed", description="Fix ranks")
|
||||
def fix_ranks(self, _, data):
|
||||
self.db.exec("UPDATE ranks set main=? where main=?", [data.new_main_id, data.old_main_id])
|
||||
self.db.exec("UPDATE account_log set char_id=? where char_id=?", [data.new_main_id, data.old_main_id])
|
||||
self.db.exec("UPDATE account_log set leader_id=? where leader_id=?", [data.new_main_id, data.old_main_id])
|
||||
|
||||
@event(event_type="buddy_logon", description="Member logon manager")
|
||||
def member_login(self, _, data):
|
||||
buddy = self.buddy_service.get_buddy(data.char_id)
|
||||
if buddy:
|
||||
if self.bot.is_ready():
|
||||
if "member" in buddy['types'] or "org_member" in buddy['types']:
|
||||
if main := self.get_account(data.char_id):
|
||||
self.event_service.fire_event(self.MEMBER_LOGON, DictObject({'account': main, 'packet': data}))
|
||||
else:
|
||||
if main := self.get_account(data.char_id):
|
||||
self.event_service.fire_event(self.MEMBER_LOGON, DictObject({'account': main, 'packet': data}))
|
||||
|
||||
@event(event_type="buddy_logoff", description="Member logoff manager")
|
||||
def member_logout(self, _, data):
|
||||
if self.bot.is_ready():
|
||||
buddy = self.buddy_service.get_buddy(data.char_id)
|
||||
if buddy:
|
||||
if "member" in buddy['types'] or "org_member" in buddy['types']:
|
||||
if main := self.get_account(data.char_id):
|
||||
self.event_service.fire_event(self.MEMBER_LOGOFF, DictObject({'account': main, 'packet': data}))
|
||||
|
||||
def get_rank_count(self, char_id):
|
||||
return self.db.query_single(
|
||||
"SELECT MAX(org_rank_id)+1 AS count FROM player WHERE org_id=(SELECT org_id FROM player where char_id=?)",
|
||||
[char_id]).count
|
||||
|
||||
def simple_checks(self, account):
|
||||
if account.get('disabled', 1) == 1:
|
||||
return False
|
||||
if account.get('member', -1) == -1:
|
||||
return False
|
||||
if self.setting_service.get_value("is_alliance_bot") == "1":
|
||||
if account.get('org_id', 0) not in self.get_orgs():
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"add_success": {
|
||||
"en_US": "Character <highlight>{char}</highlight> added as <highlight>{rank}</highlight> successfully.",
|
||||
"de_DE": "Der Charakter <highlight>{char}</highlight> wurde erfolgreich zum <highlight>{rank}</highlight> ernannt."
|
||||
},
|
||||
"add_fail": {
|
||||
"en_US": "Could not add character <highlight>{char}</highlight> as <highlight>{rank}</highlight>.",
|
||||
"de_DE": "Der Charakter <highlight>{char}</highlight> konnte nicht zum <highlight>{rank}</highlight> ernannt werden."
|
||||
},
|
||||
"rem_success": {
|
||||
"en_US": "Character <highlight>{char}</highlight> removed as <highlight>{rank}</highlight> successfully.",
|
||||
"de_DE": "Der Charakter <highlight>{char}</highlight> ist nun kein <highlight>{rank}</highlight> mehr."
|
||||
},
|
||||
"rem_fail": {
|
||||
"en_US": "Could not remove character <highlight>{char}</highlight> as <highlight>{rank}</highlight>.",
|
||||
"de_DE": "Dem Charakter <highlight>{char}</highlight> konnte der Rang <highlight>{rank}</highlight> nicht entfernt werden."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
from typing import List
|
||||
|
||||
import hjson
|
||||
|
||||
from core.buddy_service import BuddyService
|
||||
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
|
||||
from core.dict_object import DictObject
|
||||
from core.setting_service import SettingService
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
from modules.onlinebot.online.org_alias_controller import OrgAliasController
|
||||
|
||||
|
||||
@instance()
|
||||
class AdminController:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.pork_service = registry.get_instance("pork_service")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.settings_service: SettingService = registry.get_instance("setting_service")
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("adminlist", "admin")
|
||||
self.command_alias_service.add_alias("admins", "admin")
|
||||
self.ts.register_translation("module/admin", self.load_admin_msg)
|
||||
|
||||
def load_admin_msg(self):
|
||||
with open("modules/core/admin/admin.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@event(event_type="connect", description="Add admins as buddies")
|
||||
def connect_event(self, _, _1):
|
||||
for row in self.get_all():
|
||||
self.buddy_service.add_buddy(row.char_id, "admin")
|
||||
|
||||
def get_all(self) -> List[DictObject]:
|
||||
return self.db.query("SELECT p.*, COALESCE(p.name, t.char_id) AS name, t.rank, t.sort FROM "
|
||||
"("
|
||||
"SELECT a.main as char_id, a.rank, "
|
||||
"CASE WHEN rank = 'admin' THEN 0 "
|
||||
"WHEN rank = 'moderator' THEN 1 "
|
||||
"WHEN rank = 'council' THEN 2 "
|
||||
"WHEN rank = 'leader' THEN 3 END AS sort FROM ranks a) t "
|
||||
"LEFT JOIN player p ON t.char_id = p.char_id "
|
||||
"where rank in ('admin', 'moderator', 'council', 'leader') "
|
||||
"ORDER BY sort, p.name")
|
||||
|
||||
@command(command="admin", params=[Const("all")], access_level="member",
|
||||
description="Shows the administrators of the Bot", sub_command="list")
|
||||
def show_all_admins(self, _, _1):
|
||||
count = []
|
||||
# noinspection SqlAggregates
|
||||
users = self.db.query("""
|
||||
SELECT r.rank, r.rank_id, a.main, p.*, IF(o.char_id IS NULL, 0, 1) AS online
|
||||
FROM (SELECT * FROM (SELECT
|
||||
CASE when rank = 'admin' then 0
|
||||
when rank = 'moderator' then 1
|
||||
when rank = 'council' then 2
|
||||
when rank = 'leader' then 3
|
||||
ELSE 99 END AS rank_id, RANK,
|
||||
main FROM ranks ORDER BY rank_id, main LIMIT 100000) a
|
||||
GROUP BY main ORDER BY rank_id) r
|
||||
LEFT JOIN account a ON r.main = a.main
|
||||
LEFT JOIN player p ON a.char_id=p.char_id
|
||||
LEFT JOIN online o ON a.char_id=o.char_id
|
||||
WHERE a.disabled=0
|
||||
GROUP BY char_id
|
||||
ORDER BY r.rank_id, a.main desc, a.char_id = a.main DESC, p.name;""")
|
||||
blob = ""
|
||||
main = 0
|
||||
rank = ""
|
||||
ranks = {'admin': "Administrator's",
|
||||
"moderator": "Moderator's",
|
||||
"council": "Council Member's",
|
||||
"leader": "Raidleader's"}
|
||||
|
||||
for user in users:
|
||||
if user.main not in count:
|
||||
count.append(user.main)
|
||||
if user.rank != rank:
|
||||
if rank != "":
|
||||
blob += "<br>"
|
||||
rank = user.rank
|
||||
|
||||
blob += f"<notice><tab><tab>:::<tab><header>{ranks[user.rank]}</header><tab>:::</notice><br>"
|
||||
if user.online == 0:
|
||||
user.online = 1 if self.buddy_service.is_online(user.char_id) else 0
|
||||
if main != user.main:
|
||||
main = user.main
|
||||
blob += self.format_user(user)
|
||||
else:
|
||||
blob += self.format_user(user, False)
|
||||
|
||||
return ChatBlob("Administrators (%d)" % len(count), blob)
|
||||
|
||||
@command(command="admin", params=[], access_level="member",
|
||||
description="Shows the online Administrators of the bot", sub_command="list")
|
||||
def show_admin(self, _):
|
||||
count = []
|
||||
|
||||
# noinspection SqlAggregates
|
||||
users = self.db.query("""
|
||||
SELECT r.rank, r.rank_id, a.main, p.*, IF(o.char_id IS NULL, 0, 1) AS online
|
||||
FROM (SELECT * FROM (SELECT
|
||||
CASE when rank = 'admin' then 0
|
||||
when rank = 'moderator' then 1
|
||||
when rank = 'council' then 2
|
||||
when rank = 'leader' then 3
|
||||
ELSE 99 END AS rank_id, RANK,
|
||||
main FROM ranks ORDER BY rank_id, main LIMIT 100000) a
|
||||
GROUP BY main ORDER BY rank_id) r
|
||||
LEFT JOIN account a ON r.main = a.main
|
||||
LEFT JOIN player p ON a.char_id=p.char_id
|
||||
LEFT JOIN online o ON a.char_id=o.char_id
|
||||
WHERE a.disabled=0 and (o.char_id IS NOT NULL or
|
||||
(a.main = a.char_id and (SELECT count(*) from online
|
||||
where char_id in
|
||||
(SELECT char_id from account where main = r.main)) > 0))
|
||||
GROUP BY char_id
|
||||
ORDER BY r.rank_id, a.main desc, a.char_id = a.main DESC, p.name;""")
|
||||
blob = ""
|
||||
main = 0
|
||||
rank = ""
|
||||
ranks = {'admin': "Administrator's",
|
||||
"moderator": "Moderator's",
|
||||
"council": "Council Member's",
|
||||
"leader": "Raidleader's"}
|
||||
|
||||
for user in users:
|
||||
if user.main not in count:
|
||||
count.append(user.main)
|
||||
if user.rank != rank:
|
||||
if rank != "":
|
||||
blob += "<br>"
|
||||
rank = user.rank
|
||||
blob += f"<notice><tab><tab>:::<tab><header>{ranks[user.rank]}</header><tab>:::</notice><br>"
|
||||
main = ""
|
||||
if main != user.main:
|
||||
main = user.main
|
||||
blob += self.format_user(user)
|
||||
else:
|
||||
blob += self.format_user(user, False)
|
||||
|
||||
return ChatBlob("Online Administrators (%d)" % len(count), blob)
|
||||
|
||||
# noinspection LongLine
|
||||
def format_user(self, user, main=True):
|
||||
alias = self.alias_controller.get_alias(user.org_id) if self.settings_service.get_value(
|
||||
"is_alliance_bot") == "1" else user.org_name
|
||||
|
||||
if main:
|
||||
return f"<br><pagebreak>[{'<red>O</red>' if user.online == 0 else '<green>O</green>'}] <highlight>{user.name}</highlight> ({user.level}/<green>{user.ai_level}</green> - {alias}) {'[' + self.text.make_chatcmd('Tell', '/tell ' + user.name) + ']' if user.online == 1 else ''}<br>"
|
||||
return f" └ [{'<red>O</red>' if user.online == 0 else '<green>O</green>'}] <highlight>{user.name}</highlight> ({user.level}/<green>{user.ai_level}</green> - {alias}) {'[' + self.text.make_chatcmd('Tell', '/tell ' + user.name) + ']' if user.online == 1 else ''}<br>"
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"list_blob": {
|
||||
"en_US": [
|
||||
"<pagebreak>Name: <highlight>{char}</highlight>\n",
|
||||
"Added: <highlight>{added_time}</highlight>\n",
|
||||
"By: <highlight>{banner}</highlight>\n",
|
||||
"Ends: <highlight>{end_time}</highlight>{left}\n",
|
||||
"Reason: <highlight>{reason}</highlight>\n\n"
|
||||
],
|
||||
"de_DE": [
|
||||
"<pagebreak>Name: <highlight>{char}</highlight>\n",
|
||||
"Hinzugefuegt: <highlight>{added_time}</highlight>\n",
|
||||
"Von: <highlight>{banner}</highlight>\n",
|
||||
"Endet: <highlight>{end_time}</highlight>{left}\n",
|
||||
"Grund: <highlight>{reason}</highlight>\n\n"
|
||||
]
|
||||
},
|
||||
"list": {
|
||||
"en_US": "Ban List ({amount})"
|
||||
},
|
||||
"not_banned": {
|
||||
"en_US": "<highlight>{char}</highlight> is not banned.",
|
||||
"de_DE": "<highlight>{char}</highlight> ist nicht gebannt."
|
||||
},
|
||||
"unbanned_target": {
|
||||
"en_US": "You have been unbanned by <highlight>{char}</highlight>.",
|
||||
"de_DE": "Du wurdest von <highlight>{char}</highlight> entbannt."
|
||||
},
|
||||
"unbanned_self": {
|
||||
"en_US": "<highlight>{char}</highlight> has been removed from the ban list.",
|
||||
"de_DE": "Du hast <highlight>{char}</highlight> entbannt."
|
||||
},
|
||||
"already_banned": {
|
||||
"en_US": "<highlight>{char}</highlight> is already banned.",
|
||||
"de_DE": "<highlight>{char}</highlight> ist bereits gebannt."
|
||||
},
|
||||
"banned_target_1": {
|
||||
"en_US": "You have been banned by <highlight>{banner}</highlight> for reason: <highlight>{reason}</highlight>. Duration: <highlight>{duration}</highlight>.",
|
||||
"de_DE": "Du wurdest von <highlight>{banner}</highlight> gebannt. Dauer: <highlight>{duration}</highlight>. Der Grund war: <highlight>{reason}</highlight>"
|
||||
},
|
||||
"banned_target_2": {
|
||||
"en_US": "You have been banned by <highlight>{banner}</highlight>. Duration: <highlight>{duration}</highlight>.",
|
||||
"de_DE": "Du wurdest von <highlight>{banner}</highlight> gebannt - Dauer: <highlight>{duration}</highlight> gebannt."
|
||||
},
|
||||
"banned_self": {
|
||||
"en_US": "<highlight>{char}</highlight> has been added to the ban list.",
|
||||
"de_DE": "<highlight>{char}</highlight> wurde gebannt."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import hjson
|
||||
|
||||
from core.aochat.BaseModule import BaseModule
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Any, Const, Options, Time, Character
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.translation_service import TranslationService
|
||||
from core.tyrbot import Tyrbot
|
||||
from modules.core.ban.ban_service import BanService
|
||||
|
||||
|
||||
@instance()
|
||||
class BanController(BaseModule):
|
||||
# noinspection DuplicatedCode
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text = registry.get_instance("text")
|
||||
self.util = registry.get_instance("util")
|
||||
self.ban_service: BanService = registry.get_instance("ban_service")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("unban", "ban rem")
|
||||
self.ts.register_translation("module/ban", self.load_ban_msg)
|
||||
|
||||
def load_ban_msg(self):
|
||||
with open("modules/core/ban/ban.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@command(command="ban", params=[Const("list", is_optional=True)], access_level="moderator",
|
||||
description="Show the ban list")
|
||||
def ban_list_cmd(self, _, _1):
|
||||
query = self.db.query("SELECT a.*, p.*, b.reason, b.finished_at, b.created_at, b.sender_char_id from account a "
|
||||
"left join player p on a.char_id = p.char_id "
|
||||
"left join ban_list b on a.char_id = b.char_id "
|
||||
"where main=a.char_id "
|
||||
"and disabled=1 "
|
||||
"and p.org_id NOT IN (SELECT org_id from ban_org_list)")
|
||||
blob = ""
|
||||
for row in query:
|
||||
ends = "never" if (row.finished_at or -1) == -1 else self.util.format_datetime(row.finished_at)
|
||||
blob += f"<highlight>{row.name}</highlight> by " \
|
||||
f"{self.character_service.resolve_char_to_name(row.sender_char_id or self.bot.get_char_id())}\n"
|
||||
if row.sender_char_id != 0 and row.sender_char_id is not None:
|
||||
blob += f"<tab>Added at: <highlight>{self.util.format_datetime(row.created_at)}</highlight> " \
|
||||
f"Ends: <highlight>{ends}</highlight>\n"
|
||||
blob += f"<tab>Reason: {row.reason or 'None given'}\n"
|
||||
blob += "\n"
|
||||
return ChatBlob("Banned Accounts", blob)
|
||||
|
||||
@command(command="ban", params=[Options(["rem", "remove"]), Character("character")], access_level="moderator",
|
||||
description="Remove a character from the ban list")
|
||||
def ban_remove_cmd(self, _, _1, char):
|
||||
if not char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
elif not self.ban_service.get_ban(char.char_id):
|
||||
return self.getresp("module/ban", "not_banned", {"char": char.name})
|
||||
else:
|
||||
self.ban_service.remove_ban(char.char_id)
|
||||
return self.getresp("module/ban", "unbanned_self", {"char": char.name})
|
||||
|
||||
@command(command="ban",
|
||||
params=[Const("add", is_optional=True), Character("character"), Time("duration", is_optional=True),
|
||||
Any("reason", is_optional=True)], access_level="moderator",
|
||||
description="Add a character to the ban list")
|
||||
def ban_add_cmd(self, request, _, char, duration, reason):
|
||||
if not char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
elif self.ban_service.get_ban(char.char_id):
|
||||
return f"<highlight>{char.name}</highlight> is already banned."
|
||||
else:
|
||||
self.ban_service.add_ban(char.char_id, request.sender.char_id, duration, reason)
|
||||
request.reply(f"<highlight>{char.name}</highlight> has been added to the ban list.")
|
||||
@@ -0,0 +1,101 @@
|
||||
import time
|
||||
|
||||
from core.decorators import instance
|
||||
from core.dict_object import DictObject
|
||||
from core.logger import Logger
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
|
||||
|
||||
@instance()
|
||||
class BanService:
|
||||
BAN_ADDED_EVENT = "ban_added"
|
||||
BAN_REMOVED_EVENT = "ban_removed"
|
||||
|
||||
def __init__(self):
|
||||
self.logger = Logger(__name__)
|
||||
|
||||
def inject(self, registry):
|
||||
self.db = registry.get_instance("db")
|
||||
self.event_service = registry.get_instance("event_service")
|
||||
self.command_service = registry.get_instance("command_service")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
|
||||
def pre_start(self):
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS ban_list (char_id INT NOT NULL, "
|
||||
"sender_char_id INT NOT NULL, "
|
||||
"created_at INT NOT NULL, "
|
||||
"finished_at INT NOT NULL, "
|
||||
"reason VARCHAR(255) NOT NULL, "
|
||||
"ended_early SMALLINT NOT NULL, "
|
||||
"INDEX `char_id` (`char_id`) USING BTREE, "
|
||||
"INDEX `sender_char_id` (`sender_char_id`) USING BTREE)")
|
||||
self.db.exec("CREATE TABLE IF NOT EXISTS ban_org_list (org_id INT NOT NULL, "
|
||||
"sender_char_id INT NOT NULL, "
|
||||
"created_at INT NOT NULL, "
|
||||
"finished_at INT NOT NULL, "
|
||||
"reason VARCHAR(255) NOT NULL, "
|
||||
"ended_early SMALLINT NOT NULL, "
|
||||
"INDEX `org_id` (`org_id`) USING BTREE, "
|
||||
"INDEX `sender_char_id` (`sender_char_id`) USING BTREE)")
|
||||
self.event_service.register_event_type(self.BAN_ADDED_EVENT)
|
||||
self.event_service.register_event_type(self.BAN_REMOVED_EVENT)
|
||||
|
||||
def start(self):
|
||||
self.command_service.register_command_pre_processor(self.check_for_banned)
|
||||
|
||||
def add_ban(self, char_id, sender_char_id, duration=None, reason=None):
|
||||
reason = reason or ""
|
||||
|
||||
t = int(time.time())
|
||||
if duration:
|
||||
finished_at = t + duration
|
||||
else:
|
||||
finished_at = -1
|
||||
|
||||
num_rows = self.db.exec("INSERT INTO ban_list (char_id, sender_char_id, created_at, "
|
||||
"finished_at, reason, ended_early) VALUES (?, ?, ?, ?, ?, 0)",
|
||||
[char_id, sender_char_id, t, finished_at, reason])
|
||||
|
||||
if num_rows:
|
||||
self.account_service.create_users([(char_id, char_id, 0, time.time(), time.time())])
|
||||
self.db.exec("UPDATE account set disabled=1 where main=(SELECT main from account where char_id=?)",
|
||||
[char_id])
|
||||
self.event_service.fire_event(self.BAN_ADDED_EVENT, DictObject({"char_id": char_id,
|
||||
"sender_char_id": sender_char_id,
|
||||
"duration": duration,
|
||||
"reason": reason}))
|
||||
|
||||
return num_rows
|
||||
|
||||
def remove_ban(self, char_id):
|
||||
t = int(time.time())
|
||||
num_rows = self.db.exec("UPDATE ban_list SET ended_early = 1 "
|
||||
"WHERE char_id = ? AND (finished_at > ? OR finished_at = -1)", [char_id, t])
|
||||
if num_rows:
|
||||
self.db.exec("UPDATE account SET disabled = 0 "
|
||||
"WHERE main = (SELECT main from account where char_id=?)", [char_id])
|
||||
self.event_service.fire_event(self.BAN_REMOVED_EVENT, DictObject({"char_id": char_id}))
|
||||
|
||||
return num_rows
|
||||
|
||||
def get_ban(self, char_id):
|
||||
return self.db.query_single("SELECT * FROM account "
|
||||
"WHERE char_id = (SELECT main from account where char_id=?) and disabled=1",
|
||||
[char_id])
|
||||
|
||||
def get_ban_list(self):
|
||||
t = int(time.time())
|
||||
return self.db.query("SELECT b.*, COALESCE(p1.name, b.char_id) AS name, p2.name AS sender_name FROM ban_list b "
|
||||
"LEFT JOIN player p1 ON b.char_id = p1.char_id "
|
||||
"LEFT JOIN player p2 ON b.sender_char_id = p2.char_id "
|
||||
"WHERE ended_early != 1 AND (finished_at > ? OR finished_at = -1) "
|
||||
"ORDER BY b.created_at DESC", [t])
|
||||
|
||||
def check_for_banned(self, context):
|
||||
char_id = context.char_id
|
||||
if self.get_ban(char_id):
|
||||
# do nothing if character is banned
|
||||
self.logger.info("ignoring banned character %d for command '%s'" % (char_id, context.message))
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -0,0 +1,172 @@
|
||||
import time
|
||||
|
||||
import hjson
|
||||
import requests
|
||||
|
||||
from core.aochat.BaseModule import BaseModule
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Any, Const, Options, Int
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command, timerevent
|
||||
from core.logger import Logger
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.lookup.org_pork_service import OrgPorkService
|
||||
from core.private_channel_service import PrivateChannelService
|
||||
from core.translation_service import TranslationService
|
||||
from core.tyrbot import Tyrbot
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
from modules.core.ban.ban_service import BanService
|
||||
|
||||
|
||||
@instance()
|
||||
class OrgBanController(BaseModule):
|
||||
single_org_uri = "https://people.anarchy-online.com/org/stats/d/5/name/%d/basicstats.xml?data_type=json"
|
||||
|
||||
def __init__(self):
|
||||
self.logger = Logger(__name__)
|
||||
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text = registry.get_instance("text")
|
||||
self.util = registry.get_instance("util")
|
||||
self.ban_service: BanService = registry.get_instance("ban_service")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.pork: OrgPorkService = registry.get_instance("character_service")
|
||||
self.getresp = self.ts.get_response
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.priv: PrivateChannelService = registry.get_instance("private_channel_service")
|
||||
|
||||
def start(self):
|
||||
self.ts.register_translation("module/ban", self.load_ban_msg)
|
||||
|
||||
def load_ban_msg(self):
|
||||
with open("modules/core/ban/ban.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@command(command="orgban", params=[Const("list", is_optional=True)], access_level="admin",
|
||||
description="Show the ban list")
|
||||
def ban_list_cmd(self, _, _1):
|
||||
query = self.db.query("SELECT * from ban_org_list b "
|
||||
"left join all_orgs a on a.org_id = b.org_id")
|
||||
blob = ""
|
||||
for row in query:
|
||||
|
||||
blob += f"<highlight>{row.org_name}</highlight> (<highlight>{row.org_id}</highlight> " \
|
||||
f"with <highlight>{row.member_count}</highlight> members) by " \
|
||||
f"{self.character_service.resolve_char_to_name(row.sender_char_id)}\n"
|
||||
if row.sender_char_id != 0 and row.sender_char_id is not None:
|
||||
blob += f"<tab>Added at: <highlight>{self.util.format_datetime(row.created_at)}</highlight>\n"
|
||||
blob += f"<tab>Reason: {row.reason or 'None given'}\n"
|
||||
blob += "\n"
|
||||
return ChatBlob("Banned Organisations", blob)
|
||||
|
||||
@command(command="orgban", params=[Options(["rem", "remove"]), Int("Organisation")], access_level="admin",
|
||||
description="Remove an org from the ban list")
|
||||
def ban_remove_cmd(self, _, _1, org):
|
||||
if self.db.query("SELECT * from ban_org_list where org_id=?", [org]):
|
||||
self.db.exec("DELETE FROM ban_org_list where org_id = ?", [org])
|
||||
return f"Successfully unbanned org <highlight>{org}</highlight> - " \
|
||||
f"Beware, all players are still banned using an account ban; " \
|
||||
f"It can be lifted using !account add <name>"
|
||||
|
||||
@command(command="orgban", params=[Options(["rem", "remove"]), Any("Organisation")], access_level="admin",
|
||||
description="Remove an org from the ban list")
|
||||
def ban_remove_cmd_name(self, request, _, org: str):
|
||||
orgs = self.db.query(
|
||||
"SELECT * from all_orgs where org_name LIKE ? and org_id in (SELECT org_id from ban_org_list)",
|
||||
["%" + org.replace(" ", "%") + "%"])
|
||||
if len(orgs) == 1:
|
||||
self.ban_remove_cmd(request, _, orgs[0].org_id)
|
||||
elif len(orgs) == 0:
|
||||
return "No orgs matching your search found."
|
||||
elif len(orgs) > 1:
|
||||
blob = "Your search had multiple results; please pick an org:<br>"
|
||||
for org in orgs:
|
||||
blob += "[%s] <highlight>%s<end> (<highlight>%s<end>) <%s>%s<end> [<highlight>%s<end> " \
|
||||
"members]<br><pagebreak>" \
|
||||
% (self.text.make_chatcmd("Unban", "/tell <myname> orgban remove %s" % org.org_id),
|
||||
# self.text.make_chatcmd("More", "/tell <myname> org info %s" % org.org_id),
|
||||
org.org_name, org.org_id, org.faction.lower(), org.faction, org.member_count)
|
||||
return ChatBlob("Pick an Org", blob)
|
||||
|
||||
@command(command="orgban", params=[Const("add", is_optional=True), Int("Organisation"),
|
||||
Any("reason", is_optional=True)], access_level="admin",
|
||||
description="Add an org to the ban list")
|
||||
def ban_add_cmd(self, request, _, org, reason):
|
||||
if self.db.query("SELECT * from ban_org_list where org_id=?", [org]):
|
||||
return f"Organisation with the ID <highlight>{org}</highlight> is already banned."
|
||||
if self.db.exec(
|
||||
"INSERT INTO ban_org_list(org_id, sender_char_id, created_at, finished_at, reason, ended_early) "
|
||||
"VALUES (?, ?, ?, 0, ?, 0)",
|
||||
[org, request.sender.char_id, time.time(), reason or ""]):
|
||||
self.fetch_single(org, request)
|
||||
# return f"Organisation with the ID <highlight>{org}</highlight> has been banned."
|
||||
|
||||
@command(command="orgban",
|
||||
params=[Options(["add"], is_optional=True), Any("Organisation")],
|
||||
access_level="admin",
|
||||
description="Add an org from the ban list")
|
||||
def ban_add_cmd_name(self, request, _, org: str):
|
||||
orgs = self.db.query("SELECT * from all_orgs where org_name LIKE ? order by org_name",
|
||||
["%" + org.replace(" ", "%") + "%"])
|
||||
if len(orgs) == 1:
|
||||
self.ban_add_cmd(request, _, orgs[0].org_id, "")
|
||||
elif len(orgs) == 0:
|
||||
return " No orgs matching your search found."
|
||||
elif len(orgs) > 1:
|
||||
blob = "Your search had multiple results; please pick an org:<br>"
|
||||
for org in orgs:
|
||||
blob += "[%s] <highlight>%s<end> (<highlight>%s<end>) <%s>%s<end> [<highlight>%s<end> " \
|
||||
"members]<br><pagebreak>" \
|
||||
% (self.text.make_chatcmd("Add Ban", "/tell <myname> orgban add %s" % org.org_id),
|
||||
org.org_name, org.org_id, org.faction.lower(), org.faction, org.member_count)
|
||||
return ChatBlob("Pick an Org", blob)
|
||||
|
||||
def fetch_single(self, org_id, sender: object):
|
||||
start = time.time()
|
||||
data = []
|
||||
accounts = []
|
||||
count = 0
|
||||
result = requests.get(self.single_org_uri % org_id).json()
|
||||
for char_info in result[1]:
|
||||
data.append((char_info["CHAR_INSTANCE"], char_info["NAME"], char_info["FIRSTNAME"],
|
||||
char_info["LASTNAME"],
|
||||
char_info["LEVELX"], char_info["BREED"],
|
||||
char_info["SEX"], result[0]["SIDE_NAME"], char_info["PROF"],
|
||||
char_info["PROF_TITLE"], char_info["DEFENDER_RANK_TITLE"], char_info["ALIENLEVEL"],
|
||||
result[0]["ORG_INSTANCE"], result[0]["NAME"], char_info["RANK_TITLE"],
|
||||
char_info["RANK"], char_info["CHAR_DIMENSION"], char_info["HEADID"],
|
||||
0, char_info["PVPTITLE"], "roster", int(time.time())))
|
||||
|
||||
accounts.append((char_info["CHAR_INSTANCE"], char_info["CHAR_INSTANCE"], -1, start, start))
|
||||
self.priv.kick(char_info['CHAR_INSTANCE'])
|
||||
count += 1
|
||||
with self.db.pool.get_connection() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.executemany(
|
||||
"REPLACE INTO player(char_id, name, first_name, last_name, level, breed, gender, faction, "
|
||||
"profession, profession_title, ai_rank, ai_level, org_id, org_name, org_rank_name, "
|
||||
"org_rank_id, dimension, head_id, pvp_rating, pvp_title, source, last_updated) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
data)
|
||||
self.account_service.create_users(accounts, disable=True)
|
||||
if sender:
|
||||
sender.reply(
|
||||
f"Org with ID <highlight>{org_id}</highlight> has been banned. "
|
||||
f"Runtime: {time.time() - start:.2f} seconds.")
|
||||
|
||||
@timerevent(budatime="24h", description="Refresh orgbans")
|
||||
def refresh_orgban(self, _, _1):
|
||||
banned_orgs = self.db.query("SELECT * from ban_org_list")
|
||||
# for org in [1456129, 391173,1136642, 1011713]:
|
||||
# if org not in banned_orgs:
|
||||
# self.messagehub.send_message("system_logger", None, f"**WARN** Rebanning {org}",
|
||||
# f"**WARN** Rebanning {org}")
|
||||
# self.ban_add_cmd(None, None, org, 'AUTO-BAN')
|
||||
# banned_orgs.append(org)
|
||||
for org in banned_orgs:
|
||||
self.fetch_single(org.org_id, None)
|
||||
self.logger.info(f"Refreshed bans for org {org.org_id}")
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"blob_title": {
|
||||
"en_US": "Buddy list ({amount})",
|
||||
"de_DE": "Freundesliste ({amount})"
|
||||
},
|
||||
"add_success": {
|
||||
"en_US": "Character <highlight>{char}</highlight> has been added to the buddy list for type <highlight>{type}</highlight>.",
|
||||
"de_DE": "Der Charakter <highlight>{char}</highlight> wurde der Freundesliste als <highlight>{type}</highlight> hinzugefügt."
|
||||
},
|
||||
"rem_all": {
|
||||
"en_US": "Removed all <highlight>{count}</highlight> buddies from the buddy list.",
|
||||
"de_DE": "Es wurden <highlight>{count}</highlight> Freunde aus der Freundesliste entfernt."
|
||||
},
|
||||
"rem_single": {
|
||||
"en_US": "Character <highlight>{char}</highlight> has been removed from the buddy list for type <highlight>{type}</highlight>.",
|
||||
"de_DE": "Der Charakter <highlight>{char}</highlight> ist nun nicht mehr als <highlight>{type}</highlight> in der Freundesliste."
|
||||
},
|
||||
"rem_orphaned": {
|
||||
"en_US": "Removed <highlight>{count}</highlight> orphaned buddies from the buddy list.",
|
||||
"de_DE": "Es wurden <highlight>{count}</highlight> verwaiste Freunde entfernt."
|
||||
},
|
||||
"search_title": {
|
||||
"en_US": "Buddy List Search Results ({amount})",
|
||||
"de_DE": "Ergebnis der Suche in der Freundesliste ({amount})"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import hjson
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Any, Const, Options, Character
|
||||
from core.decorators import instance, command, timerevent
|
||||
from core.logger import Logger
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
@instance()
|
||||
class BuddyController:
|
||||
def __init__(self):
|
||||
self.logger = Logger(__name__)
|
||||
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.character_service = registry.get_instance("character_service")
|
||||
self.buddy_service = registry.get_instance("buddy_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
def start(self):
|
||||
self.ts.register_translation("module/buddy", self.load_buddy_msg)
|
||||
|
||||
def load_buddy_msg(self):
|
||||
with open("modules/core/buddy/buddy.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@command(command="buddylist", params=[], access_level="admin",
|
||||
description="Show characters on the buddy list")
|
||||
def buddylist_cmd(self, _):
|
||||
buddy_list = []
|
||||
for char_id, buddy in self.buddy_service.get_all_buddies().items():
|
||||
char_name = self.character_service.resolve_char_to_name(char_id, "Unknown(%d)" % char_id)
|
||||
buddy_list.append([char_name, buddy])
|
||||
|
||||
blob = self.format_buddies(buddy_list)
|
||||
|
||||
return ChatBlob(self.getresp("module/buddy", "blob_title", {"amount": len(buddy_list)}), blob)
|
||||
|
||||
@command(command="buddylist",
|
||||
params=[Const("add"), Character("character"), Any("type")],
|
||||
access_level="admin",
|
||||
description="Add a character to the buddy list")
|
||||
def buddylist_add_cmd(self, _, _1, char, buddy_type):
|
||||
buddy_type = buddy_type.lower()
|
||||
|
||||
if char.char_id:
|
||||
self.buddy_service.add_buddy(char.char_id, buddy_type)
|
||||
return self.getresp("module/buddy", "add_success", {"char": char.name, "type": buddy_type})
|
||||
else:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
|
||||
@command(command="buddylist", params=[Options(["rem", "remove"]), Const("all")], access_level="admin",
|
||||
description="Remove all characters from the buddy list")
|
||||
def buddylist_remove_all_cmd(self, _, _1, _2):
|
||||
count = 0
|
||||
for char_id, buddy in self.buddy_service.get_all_buddies().items():
|
||||
self.buddy_service.remove_buddy(char_id, None, True)
|
||||
count += 1
|
||||
|
||||
return self.getresp("module/buddy", "rem_all", {"count": count})
|
||||
|
||||
@command(command="buddylist",
|
||||
params=[Options(["rem", "remove"]), Character("character"), Any("type")],
|
||||
access_level="admin",
|
||||
description="Remove a character from the buddy list")
|
||||
def buddylist_remove_cmd(self, _, _1, char, buddy_type):
|
||||
buddy_type = buddy_type.lower()
|
||||
|
||||
if char.char_id:
|
||||
self.buddy_service.remove_buddy(char.char_id, buddy_type)
|
||||
return self.getresp("module/buddy", "rem_single", {"char": char.name, "type": buddy_type})
|
||||
else:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
|
||||
@command(command="buddylist", params=[Const("clean")], access_level="admin",
|
||||
description="Remove all orphaned buddies from the buddy list")
|
||||
def buddylist_clean_cmd(self, _, _1):
|
||||
return self.getresp("module/buddy", "rem_orphaned", {"count": self.remove_orphaned_buddies()})
|
||||
|
||||
@command(command="buddylist", params=[Const("search"), Any("character")], access_level="admin",
|
||||
description="Search for characters on the buddy list")
|
||||
def buddylist_search_cmd(self, _, _1, search):
|
||||
search = search.lower()
|
||||
|
||||
buddy_list = []
|
||||
for char_id, buddy in self.buddy_service.get_all_buddies().items():
|
||||
char_name = self.character_service.resolve_char_to_name(char_id, "Unknown(%d)" % char_id)
|
||||
if search in char_name.lower():
|
||||
buddy_list.append([char_name, buddy])
|
||||
|
||||
blob = self.format_buddies(buddy_list)
|
||||
return ChatBlob(self.getresp("module/buddy", "search_title", {"amount": len(buddy_list)}), blob)
|
||||
|
||||
@timerevent(budatime="24h", description="Remove orphaned buddies", is_hidden=True)
|
||||
def remove_orphaned_buddies_event(self, _, _1):
|
||||
self.logger.debug("removing %d orphaned buddies" % self.remove_orphaned_buddies())
|
||||
|
||||
def remove_orphaned_buddies(self):
|
||||
count = 0
|
||||
for char_id, buddy in self.buddy_service.get_all_buddies().items():
|
||||
if len(buddy["types"]) == 0:
|
||||
self.buddy_service.remove_buddy(char_id, None, True)
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def format_buddies(self, buddy_list):
|
||||
buddy_list = sorted(buddy_list, key=lambda x: x[0])
|
||||
|
||||
blob = ""
|
||||
for name, buddy in buddy_list:
|
||||
blob += "%s(%s) - %s\n" % (name, buddy["conn_id"], ",".join(buddy["types"]))
|
||||
|
||||
return blob
|
||||
@@ -0,0 +1,48 @@
|
||||
from core.aochat.BaseModule import BaseModule
|
||||
from core.decorators import instance
|
||||
from core.setting_service import SettingService
|
||||
from core.setting_types import ColorSettingType
|
||||
from core.tyrbot import Tyrbot
|
||||
|
||||
|
||||
@instance()
|
||||
class ColorController(BaseModule):
|
||||
def inject(self, registry):
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
|
||||
# noinspection LongLine
|
||||
def start(self):
|
||||
self.setting_service.register_new(self.module_name, "header_color", "#FFFF00", ColorSettingType(),
|
||||
"Color for headers")
|
||||
self.setting_service.register_new(self.module_name, "header2_color", "#FCA712", ColorSettingType(),
|
||||
"Color for sub-headers")
|
||||
self.setting_service.register_new(self.module_name, "highlight_color", "#00FF00", ColorSettingType(),
|
||||
"Color for highlight")
|
||||
self.setting_service.register_new(self.module_name, "notice_color", "#FF8C00", ColorSettingType(),
|
||||
"Color for important notices")
|
||||
self.setting_service.register_new(self.module_name, "neutral_color", "#E6E1A6", ColorSettingType(),
|
||||
"Color for neutral faction")
|
||||
self.setting_service.register_new(self.module_name, "omni_color", "#FA8484", ColorSettingType(),
|
||||
"Color for omni faction")
|
||||
self.setting_service.register_new(self.module_name, "clan_color", "#F79410", ColorSettingType(),
|
||||
"Color for clan faction")
|
||||
self.setting_service.register_new(self.module_name, "unknown_color", "#FF0000", ColorSettingType(),
|
||||
"Color for unknown faction")
|
||||
self.setting_service.register_new(self.module_name, "org_channel_color", "#89D2E8", ColorSettingType(),
|
||||
"Default org channel color")
|
||||
self.setting_service.register_new(self.module_name, "private_channel_color", "#89D2E8", ColorSettingType(),
|
||||
"Default private channel color")
|
||||
self.setting_service.register_new(self.module_name, "private_message_color", "#89D2E8", ColorSettingType(),
|
||||
"Default private message color")
|
||||
self.setting_service.register_new(self.module_name, "blob_color", "#FFFFFF", ColorSettingType(),
|
||||
"Default blob content color")
|
||||
if 'orgbot' in self.bot.modules:
|
||||
self.setting_service.register_new(self.module_name, "alliance_base", "#00FF00", ColorSettingType(),
|
||||
"Base color for alliance relay")
|
||||
self.setting_service.register_new(self.module_name, "alliance_org", "#FFFF00", ColorSettingType(),
|
||||
"Org color for alliance relay")
|
||||
self.setting_service.register_new(self.module_name, "alliance_sender", "#FF8C00", ColorSettingType(),
|
||||
"Name color for alliance relay")
|
||||
self.setting_service.register_new(self.module_name, "alliance_msg", "#FF8C00", ColorSettingType(),
|
||||
"Message color for alliance relay")
|
||||
@@ -0,0 +1,39 @@
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Any, Options
|
||||
from core.decorators import instance, command
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
@instance()
|
||||
class AliasController:
|
||||
def inject(self, registry):
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
@command(command="alias", params=[Const("list")], access_level="member",
|
||||
description="List command aliases")
|
||||
def alias_list_cmd(self, _, _1):
|
||||
blob = ""
|
||||
data = self.command_alias_service.get_enabled_aliases()
|
||||
count = len(data)
|
||||
for row in data:
|
||||
blob += row.alias + " - " + row.command + "\n"
|
||||
|
||||
return ChatBlob(self.getresp("module/config", "alias_blob_title", {"amount": count}), blob)
|
||||
|
||||
@command(command="alias", params=[Const("add"), Any("alias"), Any("command")], access_level="admin",
|
||||
description="Add a command alias", sub_command="modify")
|
||||
def alias_add_cmd(self, _, _1, alias, command_str):
|
||||
if self.command_alias_service.add_alias(alias, command_str, force_enable=True):
|
||||
return self.getresp("module/config", "alias_add_success", {"alias": alias, "cmd": command_str})
|
||||
else:
|
||||
return self.getresp("module/config", "alias_add_fail", {"alias": alias})
|
||||
|
||||
@command(command="alias", params=[Options(["rem", "remove"]), Any("alias")], access_level="admin",
|
||||
description="Remove a command alias", sub_command="modify")
|
||||
def alias_remove_cmd(self, _, _1, alias):
|
||||
if self.command_alias_service.remove_alias(alias):
|
||||
return self.getresp("module/config", "alias_rem_success", {"alias": alias})
|
||||
else:
|
||||
return self.getresp("module/config", "alias_rem_fail", {"alias": alias})
|
||||
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"alias_blob_title": {
|
||||
"en_US": "Aliases ({amount})",
|
||||
"de_DE": "Alias Liste ({amount})"
|
||||
},
|
||||
"alias_add_success": {
|
||||
"en_US": "Alias <highlight>{alias}</highlight> for command <highlight>{cmd}</highlight> added successfully.",
|
||||
"de_DE": "Der Alias <highlight>{alias}</highlight> für den Befehl <highlight>{cmd}</highlight> wurde erfolgreich hinzugefügt."
|
||||
},
|
||||
"alias_add_fail": {
|
||||
"en_US": "Cannot add alias <highlight>{alias}</highlight> since there is already an active alias with that name.",
|
||||
"de_DE": "Der Alias <highlight>{alias}</highlight> konnte nicht hinzugefügt werden, da bereits einer mit selbigem Alias existiert."
|
||||
},
|
||||
"alias_rem_success": {
|
||||
"en_US": "Alias <highlight>{alias}</highlight> has been removed successfully.",
|
||||
"de_DE": "Der Alias <highlight>{alias}</highlight> wurde erfolgreich entfernt."
|
||||
},
|
||||
"alias_rem_fail": {
|
||||
"en_US": "Could not find alias <highlight>{alias}</highlight>",
|
||||
"de_DE": "Der Alias <highlight>{alias}</highlight> konnte nicht gefunden werden."
|
||||
},
|
||||
"cmdlist_commands": {
|
||||
"en_US": "Commands ({amount})",
|
||||
"de_DE": "Befehlsliste ({amount})"
|
||||
},
|
||||
"cmd_unknown_channel": {
|
||||
"en_US": "Unknown command channel <highlight>{channel}</highlight>.",
|
||||
"de_DE": "Der Befehlschannel <highlight>{channel}</highlight> existiert nicht."
|
||||
},
|
||||
"cmd_unknown_for_channel": {
|
||||
"en_US": "Could not find command <highlight>{cmd}</highlight> for channel <highlight>{channel}</highlight>.",
|
||||
"de_DE": "Der Befehl <highlight>{cmd}</highlight> für den Channel <highlight>{channel}</highlight> wurde nicht gefunden."
|
||||
},
|
||||
"enabled_low": {
|
||||
"en_US": "enabled",
|
||||
"de_DE": "aktiviert"
|
||||
},
|
||||
"disabled_low": {
|
||||
"en_US": "disabled",
|
||||
"de_DE": "deaktiviert"
|
||||
},
|
||||
"run": {
|
||||
"en_US": "run",
|
||||
"de_DE": "ausgeführt"
|
||||
},
|
||||
"enabled_high": {
|
||||
"en_US": "<green>Enabled</green>",
|
||||
"de_DE": "<green>Aktiviert</green>"
|
||||
},
|
||||
"disabled_high": {
|
||||
"en_US": "<red>Disabled</red>",
|
||||
"de_DE": "<red>Deaktiviert</red>"
|
||||
},
|
||||
"enable": {
|
||||
"en_US": "Enable",
|
||||
"de_DE": "Aktivieren"
|
||||
},
|
||||
"disable": {
|
||||
"en_US": "Disable",
|
||||
"de_DE": "Deaktivieren"
|
||||
},
|
||||
"partial": {
|
||||
"en_US": "Partial",
|
||||
"de_DE": "Teilweise aktiviert"
|
||||
},
|
||||
"config": {
|
||||
"en_US": "Config ({count})",
|
||||
"de_DE": "Einstellungen ({count})"
|
||||
},
|
||||
"cmd_toggle_success": {
|
||||
"en_US": "Command <highlight>{cmd}</highlight> has been <highlight>{changedto}</highlight> successfully.",
|
||||
"de_DE": "Der Befehl <highlight>{cmd}</highlight> ist nun <highlight>{changedto}</highlight>"
|
||||
},
|
||||
"cmd_toggle_channel_success": {
|
||||
"en_US": "Command <highlight>{cmd}</highlight> for channel <highlight>{channel}</highlight> has been <highlight>{changedto}</highlight> successfully.",
|
||||
"de_DE": "Der Befehl <highlight>{cmd}</highlight> ist im channel <highlight>{channel}</highlight> nun <highlight>{changedto}</highlight>."
|
||||
},
|
||||
"unknown_accesslevel": {
|
||||
"en_US": "Unknown access level <highlight>{al}</highlight>.",
|
||||
"de_DE": "Unbekanntes Rechtelevel: <highlight>{al}</highlight>"
|
||||
},
|
||||
"set_accesslevel_success": {
|
||||
"en_US": "Access level <highlight>{al}</highlight> for command <highlight>{cmd}</highlight> has been set successfully.",
|
||||
"de_DE": "Für den Befehl <highlight>{cmd}</highlight> wurden die Zugriffsreche erfolgreich auf <highlight>{al}</highlight> geändert."
|
||||
},
|
||||
"set_accesslevel_fail": {
|
||||
"en_US": "Access level <highlight>{al}</highlight> for command <highlight>{cmd}</highlight> on channel <highlight>{channel}</highlight> has been set successfully.",
|
||||
"de_DE": "Der Befehl <highlight>{cmd}</highlight> benötigt im channel <highlight>{channel}</highlight> nun den Rang <highlight>{al}</highlight>."
|
||||
},
|
||||
"access_level": {
|
||||
"en_US": "Access Level",
|
||||
"de_DE": "Zugriffslevel"
|
||||
},
|
||||
"no_cmd": {
|
||||
"en_US": "Could not find command <highlight>{cmd}</highlight>.",
|
||||
"de_DE": "Der Befehl <highlight>{cmd}</highlight> ist mir unbekannt."
|
||||
},
|
||||
"settings": {
|
||||
"en_US": "<header2>Settings</header2>\n",
|
||||
"de_DE": "<header2>Einstellungen</header2>\n"
|
||||
},
|
||||
"commands": {
|
||||
"en_US": "\n<header2>Commands</header2>\n",
|
||||
"de_DE": "\n<header2>Befehle</header2>\n"
|
||||
},
|
||||
"events": {
|
||||
"en_US": "Events",
|
||||
"de_DE": "Events"
|
||||
},
|
||||
"hidden_events": {
|
||||
"en_US": "Hidden Events",
|
||||
"de_DE": "Verstecke Events"
|
||||
},
|
||||
"mod_title": {
|
||||
"en_US": "Module ({mod})",
|
||||
"de_DE": "Modul ({mod})"
|
||||
},
|
||||
"mod_not_found": {
|
||||
"en_US": "Could not find module <highlight>{mod}</highlight>",
|
||||
"de_DE": "Das Modul <highlight>{mod}</highlight> wurde nicht gefunden."
|
||||
},
|
||||
"no_new_value": {
|
||||
"en_US": "Error! New value required to update setting.",
|
||||
"de_DE": "Error! Es muss eine neue Option eingegeben werden, um die Einstellung zu ändern."
|
||||
},
|
||||
"set_clr": {
|
||||
"en_US": "Setting <highlight>{setting}</highlight> has been cleared.",
|
||||
"de_DE": "Die Einstellung <highlight>{setting}</highlight> wurde geleert."
|
||||
},
|
||||
"set_new": {
|
||||
"en_US": "Setting <highlight>{setting}</highlight> has been set to {value}.",
|
||||
"de_DE": "Die Einstellung <highlight>{setting}</highlight> hat nun den Wert: {value}"
|
||||
},
|
||||
"setting_not_found": {
|
||||
"en_US": "Could not find setting <highlight>{setting}</highlight>.",
|
||||
"de_DE": "Die Einstellung <highlight>{setting}</highlight> wurde nicht gefunden."
|
||||
},
|
||||
"current_value": {
|
||||
"en_US": "Current Value: <highlight>{value}</highlight>\n",
|
||||
"de_DE": "Aktueller Wert: <highlight>{value}</highlight>\n"
|
||||
},
|
||||
"description": {
|
||||
"en_US": "Description: <highlight>{desc}</highlight>\n\n",
|
||||
"de_DE": "Beschreibung: <highlight>{desc}</highlight>\n\n"
|
||||
},
|
||||
"setting": {
|
||||
"en_US": "Setting ({setting})",
|
||||
"de_DE": "Einstellung ({setting})"
|
||||
},
|
||||
"settinglist_title": {
|
||||
"en_US": "Settings ({count})",
|
||||
"de_DE": "Einstellungen ({count})"
|
||||
},
|
||||
"unknown_event": {
|
||||
"en_US": "Unknown event type <highlight>{type}</highlight>.",
|
||||
"de_DE": "Unbekannter Eventtyp: <highlight>{type}</highlight>"
|
||||
},
|
||||
"event_enable_fail": {
|
||||
"en_US": "Handler <highlight>{handler}</highlight> type <highlight>{type}</highlight> already has the desired type."
|
||||
},
|
||||
"event_enable_success": {
|
||||
"en_US": "Event type <highlight>{type}</highlight> for handler <highlight>{handler}</highlight> has been <highlight>{changedto}</highlight> successfully.",
|
||||
"de_DE": "Für den Eventtyp <highlight>{type}</highlight> wurde der handler <highlight>{handler}</highlight> erfolgreich <highlight>{changedto}</highlight>."
|
||||
},
|
||||
"event_manual": {
|
||||
"en_US": "Only <highlight>timer</highlight> events can be run manually.",
|
||||
"de_DE": "Es können nur <highlight>timer</highlight> Events manuell angestoßen werden."
|
||||
},
|
||||
"blob_events": {
|
||||
"en_US": "Events ({amount})",
|
||||
"de_DE": "Events ({amount})"
|
||||
},
|
||||
"hidden": {
|
||||
"en_US": "hidden",
|
||||
"de_DE": "versteckt"
|
||||
},
|
||||
"include_hidden_events": {
|
||||
"en_US": "Include hidden events",
|
||||
"de_DE": "Versteckte Events anzeigen"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import NamedParameters, Const
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
@instance()
|
||||
class CommandListController:
|
||||
def inject(self, registry):
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.command_service = registry.get_instance("command_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
@command(command="config", params=[Const("cmdlist"), NamedParameters(["access_level"])], access_level="admin",
|
||||
description="List all commands")
|
||||
def config_cmdlist_cmd(self, _, _1, named_params):
|
||||
sql = "SELECT access_level, channel, enabled, command, module, sub_command FROM command_config"
|
||||
params = []
|
||||
if named_params.access_level:
|
||||
sql += " WHERE access_level = ?"
|
||||
params.append(named_params.access_level)
|
||||
sql += " ORDER BY module, command, sub_command, channel"
|
||||
data = self.db.query(sql, params)
|
||||
|
||||
blob = ""
|
||||
current_module = ""
|
||||
current_command_key = ""
|
||||
count = 0
|
||||
temp_rows = []
|
||||
for row in data:
|
||||
if current_module != row.module:
|
||||
if temp_rows:
|
||||
blob += self.display_row_data(temp_rows)
|
||||
temp_rows = []
|
||||
blob += "\n<pagebreak><header2>%s</header2>\n" % row.module
|
||||
current_module = row.module
|
||||
current_command_key = ""
|
||||
|
||||
command_key = self.command_service.get_command_key(row.command, row.sub_command)
|
||||
if current_command_key != command_key:
|
||||
if temp_rows:
|
||||
blob += self.display_row_data(temp_rows)
|
||||
temp_rows = []
|
||||
count += 1
|
||||
blob += "%s - " % (self.text.make_tellcmd(command_key, "config cmd " + command_key))
|
||||
current_command_key = command_key
|
||||
|
||||
temp_rows.append(row)
|
||||
|
||||
if temp_rows:
|
||||
blob += self.display_row_data(temp_rows)
|
||||
|
||||
return ChatBlob(self.getresp("module/config", "cmdlist_commands", {"amount": count}), blob)
|
||||
|
||||
def display_row_data(self, rows):
|
||||
return "[%s %s]\n" % (self.get_enabled_str(rows), self.get_access_levels_str(rows))
|
||||
|
||||
def get_access_levels_str(self, rows):
|
||||
access_levels = list(map(lambda x: x.access_level, rows))
|
||||
if all(x == access_levels[0] for x in access_levels):
|
||||
return access_levels[0]
|
||||
else:
|
||||
return ",".join(access_levels)
|
||||
|
||||
def get_enabled_str(self, rows):
|
||||
enabled = list(map(lambda x: x.enabled, rows))
|
||||
|
||||
blob = ""
|
||||
if all(x == enabled[0] for x in enabled):
|
||||
blob += self.format_enabled(enabled[0])
|
||||
else:
|
||||
for x in enabled:
|
||||
blob += self.format_enabled(x)
|
||||
|
||||
return blob
|
||||
|
||||
def format_enabled(self, enabled):
|
||||
return "<green>E</green>" if enabled else "<red>D</red>"
|
||||
@@ -0,0 +1,143 @@
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Any, Options
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
@instance()
|
||||
class ConfigCommandController:
|
||||
def inject(self, registry):
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.access_service = registry.get_instance("access_service")
|
||||
self.command_service = registry.get_instance("command_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
@command(command="config",
|
||||
params=[Const("cmd"), Any("cmd_name"), Options(["enable", "disable"]), Any("channel")],
|
||||
access_level="admin",
|
||||
description="Enable or disable a command")
|
||||
def config_cmd_status_cmd(self, _, _1, cmd_name, action, cmd_channel):
|
||||
cmd_name = cmd_name.lower()
|
||||
action = action.lower()
|
||||
cmd_channel = cmd_channel.lower()
|
||||
command_str, sub_command_str = self.command_service.get_command_key_parts(cmd_name)
|
||||
enabled = 1 if action == "enable" else 0
|
||||
|
||||
if cmd_channel != "all" and not self.command_service.is_command_channel(cmd_channel):
|
||||
return self.getresp("module/config", "cmd_unknown_channel", {"channel": cmd_channel})
|
||||
|
||||
sql = "UPDATE command_config SET enabled = ? WHERE command = ? AND sub_command = ?"
|
||||
params = [enabled, command_str, sub_command_str]
|
||||
if cmd_channel != "all":
|
||||
sql += " AND channel = ?"
|
||||
params.append(cmd_channel)
|
||||
|
||||
count = self.db.exec(sql, params)
|
||||
if count == 0:
|
||||
return self.getresp("module/config", "cmd_unknown_for_channel", {"channel": cmd_channel, "cmd": cmd_name})
|
||||
else:
|
||||
action = self.getresp("module/config", "enabled_low" if action == "enable" else "disabled_low")
|
||||
if cmd_channel == "all":
|
||||
return self.getresp("module/config", "cmd_toggle_success", {"cmd": cmd_name, "changedto": action})
|
||||
else:
|
||||
return self.getresp("module/config", "cmd_toggle_channel_success",
|
||||
{"channel": cmd_channel, "cmd": cmd_name, "changedto": action})
|
||||
|
||||
@command(command="config",
|
||||
params=[Const("cmd"), Any("cmd_name"), Const("access_level"), Any("channel"), Any("access_level")],
|
||||
access_level="admin",
|
||||
description="Change access_level for a command")
|
||||
def config_cmd_access_level_cmd(self, _, _1, cmd_name, _2, cmd_channel, access_level):
|
||||
cmd_name = cmd_name.lower()
|
||||
cmd_channel = cmd_channel.lower()
|
||||
access_level = access_level.lower()
|
||||
command_str, sub_command_str = self.command_service.get_command_key_parts(cmd_name)
|
||||
|
||||
if cmd_channel != "all" and not self.command_service.is_command_channel(cmd_channel):
|
||||
return self.getresp("module/config", "cmd_unknown_channel", {"channel": cmd_channel})
|
||||
|
||||
if self.access_service.get_access_level_by_label(access_level) is None:
|
||||
return self.getresp("module/config", "unknown_accesslevel", {"al": access_level})
|
||||
|
||||
sql = "UPDATE command_config SET access_level = ? WHERE command = ? AND sub_command = ?"
|
||||
params = [access_level, command_str, sub_command_str]
|
||||
if cmd_channel != "all":
|
||||
sql += " AND channel = ?"
|
||||
params.append(cmd_channel)
|
||||
|
||||
count = self.db.exec(sql, params)
|
||||
if count == 0:
|
||||
return self.getresp("module/config", "cmd_unknown_for_channel", {"channel": cmd_channel, "cmd": cmd_name})
|
||||
else:
|
||||
if cmd_channel == "all":
|
||||
return self.getresp("module/config", "set_accesslevel_success", {"cmd": cmd_name, "al": access_level})
|
||||
else:
|
||||
return self.getresp("module/config", "set_accesslevel_fail",
|
||||
{"channel": cmd_channel, "cmd": cmd_name, "al": access_level})
|
||||
|
||||
@command(command="config", params=[Const("cmd"), Any("cmd_name")], access_level="admin",
|
||||
description="Show command configuration")
|
||||
def config_cmd_show_cmd(self, _, _1, cmd_name):
|
||||
cmd_name = cmd_name.lower()
|
||||
command_str, sub_command_str = self.command_service.get_command_key_parts(cmd_name)
|
||||
|
||||
blob = ""
|
||||
for command_channel, channel_label in self.command_service.channels.items():
|
||||
cmd_configs = self.command_service.get_command_configs(command=command_str,
|
||||
sub_command=sub_command_str,
|
||||
channel=command_channel,
|
||||
enabled=None)
|
||||
if len(cmd_configs) > 0:
|
||||
cmd_config = cmd_configs[0]
|
||||
status = self.getresp("module/config", "enabled_high" if cmd_config.enabled == 1 else "disabled_high")
|
||||
|
||||
blob += "<header2>%s</header2> %s (%s: %s)\n" % (channel_label, status,
|
||||
self.getresp("module/config", "access_level"),
|
||||
cmd_config.access_level.capitalize())
|
||||
|
||||
# show status config
|
||||
blob += "Status:"
|
||||
enable_link = self.text.make_tellcmd(self.getresp("module/config", "enable"),
|
||||
"config cmd %s enable %s"
|
||||
% (cmd_name, command_channel))
|
||||
disable_link = self.text.make_tellcmd(self.getresp("module/config", "disable"),
|
||||
"config cmd %s disable %s"
|
||||
% (cmd_name, command_channel))
|
||||
|
||||
blob += " " + enable_link + " " + disable_link
|
||||
|
||||
# show access level config
|
||||
blob += "\n" + self.getresp("module/config", "access_level")
|
||||
for access_level in self.access_service.access_levels:
|
||||
# skip "None" access level
|
||||
if access_level["level"] == 0:
|
||||
continue
|
||||
|
||||
label = access_level["label"]
|
||||
link = self.text.make_tellcmd(label.capitalize(),
|
||||
f"config cmd {cmd_name} access_level {command_channel} {label}")
|
||||
blob += " " + link
|
||||
blob += "\n\n"
|
||||
|
||||
if blob:
|
||||
sub_commands = self.get_sub_commands(command_str, sub_command_str)
|
||||
if sub_commands:
|
||||
blob += "<header2>Subcommands</header2>\n"
|
||||
for row in sub_commands:
|
||||
command_name = self.command_service.get_command_key(row.command, row.sub_command)
|
||||
blob += self.text.make_tellcmd(command_name, f"config cmd {command_name}") + "\n\n"
|
||||
|
||||
# include help text
|
||||
blob += "\n\n".join(map(lambda handler: handler["help"], self.command_service.get_handlers(cmd_name)))
|
||||
return ChatBlob("Command (%s)" % cmd_name, blob)
|
||||
else:
|
||||
return self.getresp("module/config", "no_cmd", {"cmd": cmd_name})
|
||||
|
||||
def get_sub_commands(self, command_str, sub_command_str):
|
||||
return self.db.query("SELECT DISTINCT command, sub_command FROM command_config "
|
||||
"WHERE command = ? AND sub_command != ?",
|
||||
[command_str, sub_command_str])
|
||||
@@ -0,0 +1,193 @@
|
||||
import hjson
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Any, Options, NamedFlagParameters
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
# noinspection SqlCaseVsIf
|
||||
@instance()
|
||||
class ConfigController:
|
||||
def inject(self, registry):
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.command_service = registry.get_instance("command_service")
|
||||
self.event_service = registry.get_instance("event_service")
|
||||
self.setting_service = registry.get_instance("setting_service")
|
||||
self.config_events_controller = registry.get_instance("config_events_controller")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
def start(self):
|
||||
self.ts.register_translation("module/config", self.load_config_msg)
|
||||
|
||||
def load_config_msg(self):
|
||||
with open("modules/core/config/config.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@command(command="config", params=[], access_level="admin",
|
||||
description="Show configuration options for the bot")
|
||||
def config_list_cmd(self, _):
|
||||
sql = """SELECT
|
||||
module,
|
||||
SUM(CASE WHEN enabled = 1 THEN 1 ELSE 0 END) count_enabled,
|
||||
SUM(CASE WHEN enabled = 0 THEN 1 ELSE 0 END) count_disabled
|
||||
FROM
|
||||
(SELECT module, enabled FROM command_config
|
||||
UNION
|
||||
SELECT module, enabled FROM event_config WHERE is_hidden = 0
|
||||
UNION
|
||||
SELECT module, 2 FROM setting) t
|
||||
GROUP BY
|
||||
module
|
||||
ORDER BY
|
||||
module"""
|
||||
|
||||
data = self.db.query(sql)
|
||||
count = len(data)
|
||||
blob = ""
|
||||
current_group = ""
|
||||
for row in data:
|
||||
parts = row.module.split(".")
|
||||
group = parts[0]
|
||||
module = parts[1]
|
||||
if group != current_group:
|
||||
current_group = group
|
||||
blob += "\n<header2>" + current_group + "</header2>\n"
|
||||
|
||||
blob += self.text.make_tellcmd(module, "config mod " + row.module) + " "
|
||||
if row.count_enabled > 0 and row.count_disabled > 0:
|
||||
blob += self.getresp("module/config", "partial")
|
||||
else:
|
||||
blob += f"[{'<green>Enabled</green>' if row.count_disabled == 0 else '<red>Disabled</red>'}]"
|
||||
blob += "\n"
|
||||
|
||||
return ChatBlob(self.getresp("module/config", "config", {"count": count}), blob)
|
||||
|
||||
@command(command="config",
|
||||
params=[Options(["mod", "module"]), Any("module_name"), NamedFlagParameters(["include_hidden_events"])],
|
||||
access_level="admin",
|
||||
description="Show configuration options for a specific module")
|
||||
def config_module_list_cmd(self, _, _1, module, named_params):
|
||||
module = module.lower()
|
||||
|
||||
blob = ""
|
||||
|
||||
data = self.db.query("SELECT name FROM setting WHERE module = ? ORDER BY name", [module])
|
||||
if data:
|
||||
blob += self.getresp("module/config", "settings")
|
||||
for row in data:
|
||||
setting = self.setting_service.get(row.name)
|
||||
blob += "%s: %s (%s)\n" % (setting.get_description(), setting.get_display_value(),
|
||||
self.text.make_tellcmd("change", "config setting " + row.name))
|
||||
|
||||
data = self.db.query(
|
||||
"SELECT DISTINCT command, sub_command FROM command_config WHERE module = ? ORDER BY command", [module])
|
||||
if data:
|
||||
blob += self.getresp("module/config", "commands")
|
||||
for row in data:
|
||||
command_key = self.command_service.get_command_key(row.command, row.sub_command)
|
||||
blob += self.text.make_tellcmd(command_key, "config cmd " + command_key) + "\n"
|
||||
|
||||
blob += self.format_events(self.get_events(module, False), self.getresp("module/config", "events"))
|
||||
|
||||
if named_params.include_hidden_events:
|
||||
blob += self.format_events(self.get_events(module, True), self.getresp("module/config", "hidden_events"))
|
||||
|
||||
if blob:
|
||||
if not named_params.include_hidden_events:
|
||||
blob += "\n" + self.text.make_tellcmd(self.getresp("module/config", "include_hidden_events"),
|
||||
f"config mod {module} --include_hidden_events")
|
||||
|
||||
return ChatBlob(self.getresp("module/config", "mod_title", {"mod": module}), blob)
|
||||
else:
|
||||
return self.getresp("module/config", "mod_not_found", {"mod": module})
|
||||
|
||||
@command(command="config", params=[Const("settinglist")], access_level="admin",
|
||||
description="List all settings")
|
||||
def config_settinglist_cmd(self, _, _1):
|
||||
blob = ""
|
||||
|
||||
data = self.db.query("SELECT * FROM setting ORDER BY module, name")
|
||||
count = len(data)
|
||||
if data:
|
||||
blob += self.getresp("module/config", "settings")
|
||||
current_module = ""
|
||||
for row in data:
|
||||
if row.module != current_module:
|
||||
current_module = row.module
|
||||
blob += "\n<pagebreak><header2>%s</header2>\n" % row.module
|
||||
|
||||
setting = self.setting_service.get(row.name)
|
||||
blob += "%s: %s (%s)\n" % (setting.get_description(),
|
||||
setting.get_display_value(),
|
||||
self.text.make_tellcmd("change", "config setting " + row.name))
|
||||
|
||||
return ChatBlob(self.getresp("module/config", "settinglist_title", {"count": count}), blob)
|
||||
|
||||
@command(command="config", params=[Const("setting"), Any("setting_name"), Options(["set", "clear"]),
|
||||
Any("new_value", is_optional=True)], access_level="admin",
|
||||
description="Change a setting value")
|
||||
def config_setting_update_cmd(self, _, _1, setting_name, op, new_value):
|
||||
setting_name = setting_name.lower()
|
||||
|
||||
if op == "clear":
|
||||
new_value = ""
|
||||
elif not new_value:
|
||||
return self.getresp("module/config", "no_new_value")
|
||||
setting = self.setting_service.get(setting_name)
|
||||
|
||||
if setting:
|
||||
setting.set_value(new_value)
|
||||
if op == "clear":
|
||||
return self.getresp("module/config", "set_clr", {"setting": setting_name})
|
||||
else:
|
||||
return self.getresp("module/config", "set_new", {"setting": setting_name,
|
||||
"value": setting.get_display_value()})
|
||||
else:
|
||||
return self.getresp("module/config", "setting_not_found", {"setting": setting_name})
|
||||
|
||||
@command(command="config", params=[Const("setting"), Any("setting_name")], access_level="admin",
|
||||
description="Show configuration options for a setting")
|
||||
def config_setting_show_cmd(self, _, _1, setting_name):
|
||||
setting_name = setting_name.lower()
|
||||
|
||||
blob = ""
|
||||
|
||||
setting = self.setting_service.get(setting_name)
|
||||
|
||||
if setting:
|
||||
blob += self.getresp("module/config", "current_value", {"value": str(setting.get_display_value())})
|
||||
blob += self.getresp("module/config", "description", {"desc": setting.get_description()})
|
||||
if setting.get_extended_description():
|
||||
blob += setting.get_extended_description() + "\n\n"
|
||||
blob += setting.get_display()
|
||||
return ChatBlob(self.getresp("module/config", "setting", {"setting": setting_name}), blob)
|
||||
else:
|
||||
return self.getresp("module/config", "setting_not_found", {"setting": setting_name})
|
||||
|
||||
def get_events(self, module, is_hidden):
|
||||
return self.db.query("SELECT event_type, event_sub_type, handler, description, enabled, is_hidden "
|
||||
f"FROM event_config WHERE module = ? AND is_hidden = ? "
|
||||
"ORDER BY is_hidden, event_type, handler",
|
||||
[module, 1 if is_hidden else 0])
|
||||
|
||||
def format_events(self, data, title):
|
||||
blob = ""
|
||||
if data:
|
||||
blob += f"\n<header2>{title}</header2>\n"
|
||||
for row in data:
|
||||
event_type_key = self.event_service.get_event_type_key(row.event_type, row.event_sub_type)
|
||||
enabled = self.getresp("module/config", "enabled_high" if row.enabled == 1 else "disabled_high")
|
||||
blob += f"{self.config_events_controller.format_event_type(row)} - {row.description} [{enabled}]"
|
||||
blob += " " + self.text.make_tellcmd("On", "config event %s %s enable" % (event_type_key, row.handler))
|
||||
blob += " " + self.text.make_tellcmd("Off",
|
||||
"config event %s %s disable" % (event_type_key, row.handler))
|
||||
if row.event_type == "timer":
|
||||
blob += " " + self.text.make_tellcmd("Run Now",
|
||||
"config event %s %s run" % (event_type_key, row.handler))
|
||||
blob += "\n"
|
||||
return blob
|
||||
@@ -0,0 +1,110 @@
|
||||
import time
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Any, Options, NamedParameters
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
@instance()
|
||||
class ConfigEventsController:
|
||||
def inject(self, registry):
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.command_service = registry.get_instance("command_service")
|
||||
self.event_service = registry.get_instance("event_service")
|
||||
self.setting_service = registry.get_instance("setting_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
@command(command="config",
|
||||
params=[Const("event"), Any("event_type"), Any("event_handler"), Options(["enable", "disable"])],
|
||||
access_level="admin",
|
||||
description="Enable or disable an event")
|
||||
def config_event_status_cmd(self, _, _1, event_type, event_handler, action):
|
||||
event_type = event_type.lower()
|
||||
event_handler = event_handler.lower()
|
||||
action = action.lower()
|
||||
event_base_type, event_sub_type = self.event_service.get_event_type_parts(event_type)
|
||||
enabled = 1 if action == "enable" else 0
|
||||
|
||||
if not self.event_service.is_event_type(event_base_type):
|
||||
return self.getresp("module/config", "unknown event", {"type", event_type})
|
||||
|
||||
count = self.event_service.update_event_status(event_base_type, event_sub_type, event_handler, enabled)
|
||||
|
||||
if count == 0:
|
||||
return self.getresp("module/config", "event_enable_fail", {"type": event_type, "handler": event_handler})
|
||||
else:
|
||||
action = self.getresp("module/config", "enabled_high" if action == "enable" else "disabled_high")
|
||||
return self.getresp("module/config", "event_enable_success", {"type": event_type,
|
||||
"handler": event_handler,
|
||||
"changedto": action})
|
||||
|
||||
@command(command="config",
|
||||
params=[Const("event"), Any("event_type"), Any("event_handler"), Const("run")],
|
||||
access_level="admin",
|
||||
description="Execute a timed event immediately")
|
||||
def config_event_run_cmd(self, _, _1, event_type, event_handler, _2):
|
||||
event_type = event_type.lower()
|
||||
event_handler = event_handler.lower()
|
||||
event_base_type, event_sub_type = self.event_service.get_event_type_parts(event_type)
|
||||
|
||||
if not self.event_service.is_event_type(event_base_type):
|
||||
return self.getresp("module/config", "unknown event", {"type", event_type})
|
||||
|
||||
row = self.db.query_single("SELECT e.event_type, e.event_sub_type, e.handler, t.next_run FROM timer_event t "
|
||||
"JOIN event_config e ON t.event_type = e.event_type AND t.handler = e.handler "
|
||||
"WHERE e.event_type = ? AND e.event_sub_type = ? AND e.handler LIKE ?",
|
||||
[event_base_type, event_sub_type, event_handler])
|
||||
|
||||
if not row:
|
||||
return self.getresp("module/config", "event_enable_fail", {"type": event_type, "handler": event_handler})
|
||||
elif row.event_type != "timer":
|
||||
return self.getresp("module/config", "event_manual")
|
||||
else:
|
||||
self.event_service.execute_timed_event(row, int(time.time()))
|
||||
action = self.getresp("module/config", "run")
|
||||
return self.getresp("module/config", "event_enable_success", {"type": event_type,
|
||||
"handler": event_handler,
|
||||
"changedto": action})
|
||||
|
||||
@command(command="config", params=[Const("eventlist"), NamedParameters(["event_type"])], access_level="admin",
|
||||
description="List all events")
|
||||
def config_eventlist_cmd(self, _, _1, named_params):
|
||||
params = []
|
||||
sql = "SELECT module, event_type, event_sub_type, handler, description, enabled, is_hidden FROM event_config"
|
||||
if named_params.event_type:
|
||||
sql += " WHERE event_type = ?"
|
||||
params.append(named_params.event_type)
|
||||
sql += " ORDER BY module, is_hidden, event_type, event_sub_type, handler"
|
||||
data = self.db.query(sql, params)
|
||||
|
||||
blob = "Asterisk (*) denotes a hidden event. Only change these events if you understand the implications.\n"
|
||||
current_module = ""
|
||||
for row in data:
|
||||
if current_module != row.module:
|
||||
blob += "\n<pagebreak><header2>%s</header2>\n" % row.module
|
||||
current_module = row.module
|
||||
|
||||
event_type_key = self.format_event_type(row)
|
||||
|
||||
on_link = self.text.make_tellcmd("On", "config event %s %s enable" % (event_type_key, row.handler))
|
||||
off_link = self.text.make_tellcmd("Off", "config event %s %s disable" % (event_type_key, row.handler))
|
||||
|
||||
if row.is_hidden == 1:
|
||||
blob += "*"
|
||||
blob += f"{event_type_key} [{self.format_enabled(row.enabled)}] {on_link} {off_link} - {row.description}\n"
|
||||
|
||||
return ChatBlob(self.getresp("module/config", "blob_events", {"amount": len(data)}), blob)
|
||||
|
||||
def format_enabled(self, enabled):
|
||||
return "<green>E</green>" if enabled else "<red>D</red>"
|
||||
|
||||
def format_event_type(self, row):
|
||||
if row.event_sub_type:
|
||||
return row.event_type + ":" + row.event_sub_type
|
||||
else:
|
||||
return row.event_type
|
||||
@@ -0,0 +1,704 @@
|
||||
import asyncio
|
||||
import html
|
||||
import logging
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
from asyncio import BaseEventLoop
|
||||
from html.parser import HTMLParser
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import discord
|
||||
import emojis as emojis
|
||||
# noinspection PyPackageRequirements
|
||||
from discord import Message, TextChannel, Guild, Embed, Role
|
||||
|
||||
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 core.lookup.character_service import CharacterService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.message_hub_service import MessageHubService
|
||||
from core.setting_service import SettingService
|
||||
from core.setting_types import HiddenSettingType
|
||||
from core.text import Text
|
||||
from core.tyrbot import Tyrbot
|
||||
from core.util import Util
|
||||
from modules.core.accounting.services.access_service import AccessService
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
from modules.core.ban.ban_service import BanService
|
||||
from modules.onlinebot.online.org_alias_controller import OrgAliasController
|
||||
|
||||
|
||||
class MLStripper(HTMLParser):
|
||||
def error(self, message):
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.reset()
|
||||
self.strict = False
|
||||
self.convert_charrefs = True
|
||||
self.fed = []
|
||||
|
||||
def handle_data(self, d):
|
||||
self.fed.append(d)
|
||||
|
||||
def get_data(self):
|
||||
return "".join(self.fed)
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@instance()
|
||||
class DiscordController:
|
||||
MESSAGE_SOURCE = "discord"
|
||||
|
||||
def __init__(self):
|
||||
self.logger = Logger(__name__)
|
||||
logging.getLogger("discord.gateway").setLevel(logging.WARN)
|
||||
logging.getLogger("discord.client").setLevel(logging.WARN)
|
||||
intents = discord.Intents.all()
|
||||
self.client = discord.Client(intents=intents, chunk_guilds_at_startup=True)
|
||||
self.client.event(self.on_ready)
|
||||
self.client.event(self.on_member_join)
|
||||
self.client.event(self.on_member_remove)
|
||||
self.client.event(self.on_member_update)
|
||||
self.client.event(self.on_guild_role_delete)
|
||||
self.client.event(self.on_guild_role_create)
|
||||
self.client.event(self.on_guild_role_update)
|
||||
self.client.event(self.on_invite_create)
|
||||
self.client.event(self.on_invite_delete)
|
||||
self.client.event(self.on_message)
|
||||
self.guild = None
|
||||
self.channel = None
|
||||
self.thread = None
|
||||
self.invites = None
|
||||
self.discord_roles = DictObject(
|
||||
{"override": "Override",
|
||||
"admin": "Administrator",
|
||||
"council": "Council",
|
||||
"leader": "Raid Leader",
|
||||
"president": "President",
|
||||
"general": "General",
|
||||
"officer": "Officer",
|
||||
"member": "Member",
|
||||
"failed": "failed"})
|
||||
# logging.getLogger("discord").setLevel(logging.INFO)
|
||||
self.discord_queue = []
|
||||
self.ao_queue = []
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.access_service: AccessService = registry.get_instance("access_service")
|
||||
self.pork: PorkService = registry.get_instance("pork_service")
|
||||
self.org: PorkService = registry.get_instance("pork_service")
|
||||
self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service")
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
|
||||
def pre_start(self):
|
||||
self.setting_service.register_new(self.module_name, "discord_token", "", HiddenSettingType(allow_empty=True),
|
||||
"Enter your Discord token her")
|
||||
|
||||
def get_name(self, discord_id):
|
||||
data = self.db.query_single(
|
||||
"SELECT d.*, p.* FROM account d LEFT JOIN player p on d.main=p.char_id where discord_id=?", [discord_id])
|
||||
return data.name if data else None
|
||||
|
||||
def setting_discord_token(self):
|
||||
return self.setting_service.get("discord_token")
|
||||
|
||||
@command(command="discord", params=[Const("invite")], access_level="member",
|
||||
description="Get a personal Discord invite", sub_command="invite")
|
||||
def discord_invite_cmd(self, request, _):
|
||||
if not self.client:
|
||||
return "Discord module has not been initiated yet. Please try again later."
|
||||
account = self.account_service.get_account(request.sender.char_id)
|
||||
if account is None:
|
||||
return "You do not have an account"
|
||||
if account.disabled == 1:
|
||||
return "Your account is disabled"
|
||||
if account.discord_joined == 1:
|
||||
return "You have already joined the Discord server"
|
||||
if account.discord_invite != "":
|
||||
for ginvite in self.invites:
|
||||
if ginvite.code == account.discord_invite:
|
||||
asyncio.run_coroutine_threadsafe(self.discord_delete_invite(ginvite), self.loop)
|
||||
invite = asyncio.run_coroutine_threadsafe(self.discord_create_invite(account.name), self.loop)
|
||||
invite = invite.result()
|
||||
self.set_discord_invite(account.char_id, invite.code)
|
||||
self.bot.send_mass_message(request.sender.char_id,
|
||||
f"Your personal Discord invite is: {invite} \n"
|
||||
f"This invite is only valid for 5 minutes.")
|
||||
|
||||
@command(command="discord", params=[Const("update")], access_level="member",
|
||||
description="Update your discord information", sub_command="update")
|
||||
def discord_update_cmd(self, request, _):
|
||||
if not self.client:
|
||||
return "Discord module has not been initiated yet. Please try again later."
|
||||
return self.discord_update_account(request.sender.char_id)
|
||||
|
||||
@command(command="discord", params=[Const("disconnect")], access_level="admin",
|
||||
description="Disconnect from Discord", sub_command="admin")
|
||||
def discord_disconnect_cmd(self, _, _1):
|
||||
if not self.client:
|
||||
return "Discord module has not been initiated yet. Please try again later."
|
||||
if self.client.is_closed():
|
||||
return "Discord is already disconnected"
|
||||
else:
|
||||
asyncio.run_coroutine_threadsafe(self.discord_disconnect(), self.loop)
|
||||
return "Disconnecting Discord"
|
||||
|
||||
@command(command="discord",
|
||||
params=[Const("members")],
|
||||
access_level="admin",
|
||||
description="Get all discord members",
|
||||
sub_command="members")
|
||||
def discord_members_cmd(self, _, _1):
|
||||
if not self.client:
|
||||
return "Discord module has not been initiated yet. Please try again later."
|
||||
blob = ""
|
||||
for member in self.guild.members:
|
||||
member_roles = []
|
||||
for role in member.roles:
|
||||
if role.name == "@everyone": # Skip @everyone as everyone has it.
|
||||
continue
|
||||
member_roles.append("%s" % role.name)
|
||||
member_roles.sort(key=str.lower)
|
||||
blob += "%s (%s)\n" % (member.name + "#" + member.discriminator, ", ".join(member_roles))
|
||||
return ChatBlob("Discord Members (%d)" % (len(self.guild.members)), blob)
|
||||
|
||||
@event(event_type="connect", description="Connects the Discord client automatically on startup, if a token exists")
|
||||
def handle_connect_event(self, _, _1):
|
||||
token = self.setting_discord_token().get_value()
|
||||
if token == "None":
|
||||
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 == "None":
|
||||
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 == "None":
|
||||
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 in ["None", "", "NULL", None]:
|
||||
return
|
||||
if not self.bot.is_ready():
|
||||
return
|
||||
update = asyncio.run_coroutine_threadsafe(self.discord_update_bot_basic(), self.loop)
|
||||
# Update accounts that have left/joined discord without the bot being online
|
||||
accounts = self.db.query("SELECT * FROM account WHERE discord_id !=''")
|
||||
for account in accounts:
|
||||
match = False
|
||||
for member in self.guild.members:
|
||||
handle = member.name + "#" + member.discriminator
|
||||
if member.id == int(account.discord_id):
|
||||
match = True
|
||||
if account.discord_joined == 0:
|
||||
self.set_discord_joined(account.main, handle, member.id)
|
||||
break
|
||||
if match is False:
|
||||
if account.discord_joined == 1:
|
||||
self.set_discord_left(account.main)
|
||||
# Update current discord Members
|
||||
accounts = self.db.query("SELECT * FROM account WHERE discord_joined = 1 and char_id = main")
|
||||
for member in self.guild.members:
|
||||
if member.id == self.client.user.id:
|
||||
continue
|
||||
member_account = None
|
||||
|
||||
for account in list(accounts):
|
||||
if int(account.discord_id) == member.id:
|
||||
member_account = account
|
||||
accounts.remove(account)
|
||||
break
|
||||
access_level = 0
|
||||
if member_account is not None:
|
||||
access_level = self.access_service.get_access_level(member_account.main)
|
||||
roles = asyncio.run_coroutine_threadsafe(self.discord_member_roles(member, member_account, access_level),
|
||||
self.loop)
|
||||
if member_account is not None:
|
||||
main = self.pork.get_character_info(member_account.main)
|
||||
# noinspection LongLine
|
||||
nick = f"{f'[{self.alias_controller.get_alias(main.org_id)}] ' if self.setting_service.get_value('is_alliance_bot') == '1' else ''}{main.name}"
|
||||
|
||||
nick = asyncio.run_coroutine_threadsafe(self.discord_member_nick(member, nick), self.loop)
|
||||
|
||||
def run_it_forever(self):
|
||||
self.loop.run_forever()
|
||||
|
||||
def set_discord_invite(self, char_id, invite):
|
||||
return self.db.exec("UPDATE account SET discord_invite = ? WHERE main = ?", [invite, char_id])
|
||||
|
||||
def set_discord_joined(self, char_id, handle, discord_id):
|
||||
return self.db.exec("UPDATE account SET discord_joined = 1, discord_handle = ?, discord_id = ? WHERE main = ?",
|
||||
[handle, discord_id, char_id])
|
||||
|
||||
def set_discord_left(self, char_id):
|
||||
return self.db.exec("UPDATE account SET discord_joined = 0 WHERE main = ?", [char_id])
|
||||
|
||||
def set_discord_handle(self, discord_id, handle):
|
||||
return self.db.exec("UPDATE account SET discord_handle = ? WHERE discord_id = ?", [handle, discord_id])
|
||||
|
||||
def get_discord_invite(self, invite):
|
||||
return self.db.query_single("SELECT * FROM account WHERE discord_invite = ?", [invite])
|
||||
|
||||
def get_account_discord_id(self, discord_id):
|
||||
return self.db.query_single(
|
||||
"SELECT * FROM account a left join player p on a.char_id=p.char_id WHERE discord_id = ?", [discord_id])
|
||||
|
||||
def discord_update_account(self, char_id):
|
||||
account = self.account_service.get_account(char_id)
|
||||
if account is None:
|
||||
return
|
||||
if account.discord_id != "" and account.discord_joined == 1:
|
||||
member = self.guild.get_member(int(account.discord_id))
|
||||
if member is not None:
|
||||
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
|
||||
nick = f"{f'[{self.alias_controller.get_alias(account.org_id)}] ' if self.setting_service.get_value('is_alliance_bot') == '1' else ''}{account.name}"
|
||||
nick = asyncio.run_coroutine_threadsafe(self.discord_member_nick(member, nick), self.loop)
|
||||
return "Processing..."
|
||||
return "No Discord account found"
|
||||
|
||||
def discord_invite_used(self, temp_invites):
|
||||
for temp_invite in temp_invites:
|
||||
# if temp_invite.inviter.id == self.client.user.id:
|
||||
# ## Using this line limits it to invite created by the account the Discord bot runs as..
|
||||
for invite in self.invites:
|
||||
if int(temp_invite.uses) > int(invite.uses) and temp_invite.code == invite.code:
|
||||
return temp_invite
|
||||
return None
|
||||
|
||||
def get_role(self, name, roles) -> Role or None:
|
||||
for role in roles:
|
||||
if role.name == name:
|
||||
return role
|
||||
return None
|
||||
|
||||
async def on_member_join(self, member):
|
||||
temp_invites = await self.guild.invites()
|
||||
invite_used = self.discord_invite_used(temp_invites)
|
||||
self.discord_queue.append(DictObject({"type": "on_member_join", "member": member, "invite": invite_used}))
|
||||
await invite_used.delete()
|
||||
|
||||
async def on_member_remove(self, member):
|
||||
self.discord_queue.append(DictObject({"type": "on_member_remove", "member": member}))
|
||||
await self.discord_update_bot_full()
|
||||
|
||||
async def on_ready(self):
|
||||
await self.discord_update_bot_full()
|
||||
count = self.db.query_single('SELECT count(*) as count from online '
|
||||
'where char_id NOT IN (select char_id from org_bots) and bot=?',
|
||||
[self.bot.get_char_id()]).count
|
||||
|
||||
act = discord.Activity(type=discord.ActivityType.listening,
|
||||
name=f"{count} Players")
|
||||
asyncio.run_coroutine_threadsafe(self.client.change_presence(activity=act), self.loop)
|
||||
await self.clean_channel()
|
||||
|
||||
if self.guild.large:
|
||||
self.logger.error(
|
||||
f"Guild {self.guild.name} is classified as large, "
|
||||
f"you need to request offline members to manage roles properly, this is not yet implemented")
|
||||
|
||||
async def on_member_update(self, member_before, member_after):
|
||||
await self.discord_update_bot_basic()
|
||||
|
||||
async def on_guild_role_create(self, role):
|
||||
await self.discord_update_bot_full()
|
||||
|
||||
async def on_guild_role_delete(self, role):
|
||||
await self.discord_update_bot_full()
|
||||
|
||||
async def on_guild_role_update(self, role_before, role_after):
|
||||
await self.discord_update_bot_full()
|
||||
|
||||
async def on_invite_create(self, invite):
|
||||
await self.discord_update_bot_full()
|
||||
|
||||
async def on_invite_delete(self, invite):
|
||||
await self.discord_update_bot_full()
|
||||
|
||||
async def discord_update_bot_basic(self):
|
||||
self.guild = self.client.get_guild(self.client.guilds[0].id)
|
||||
self.channel = self.client.get_channel(self.guild.text_channels[0].id)
|
||||
|
||||
async def discord_update_bot_full(self):
|
||||
await self.discord_update_bot_basic()
|
||||
self.invites = await self.guild.invites()
|
||||
|
||||
async def discord_disconnect(self):
|
||||
await self.client.close()
|
||||
|
||||
async def discord_connect(self, token):
|
||||
try:
|
||||
if not self.client.is_closed():
|
||||
self.logger.info("Logging into Discord...")
|
||||
await self.client.start(token)
|
||||
|
||||
except discord.ClientException as exc:
|
||||
self.logger.error("Something broke, I'm out!: %s" % str(exc))
|
||||
|
||||
async def discord_create_invite(self, reason=""):
|
||||
created_invite = await self.channel.create_invite(max_age=300, max_uses=2, reason=reason)
|
||||
self.guild = self.client.get_guild(self.client.guilds[0].id)
|
||||
return created_invite
|
||||
|
||||
async def discord_delete_invite(self, invite):
|
||||
await invite.delete()
|
||||
|
||||
async def discord_banlist(self):
|
||||
return await self.guild.bans()
|
||||
|
||||
async def discord_ban_user(self, user, reason=""):
|
||||
await self.guild.ban(user, reason=reason)
|
||||
|
||||
async def discord_unban_user(self, user):
|
||||
await self.guild.unban(user)
|
||||
|
||||
async def discord_member_nick(self, account, nick):
|
||||
if self.get_role(self.discord_roles.override, account.roles):
|
||||
return # We do not process accounts that have `override` role
|
||||
if account.nick != nick:
|
||||
result = await account.edit(nick=nick)
|
||||
|
||||
async def discord_member_roles(self, discord_user: discord.Member, account, access_level):
|
||||
if self.get_role(self.discord_roles.override, discord_user.roles):
|
||||
return # We do not process accounts that have `override` role
|
||||
addroles = []
|
||||
remroles = []
|
||||
if account is None: # We assign failed role
|
||||
discrole = self.get_role(self.discord_roles.failed, discord_user.roles)
|
||||
self.logger.info("%s role result is %s" % (discord_user.nick, discrole))
|
||||
if discrole is None:
|
||||
failed = self.get_role(self.discord_roles.failed, self.guild.roles)
|
||||
if failed is None:
|
||||
return
|
||||
addroles.append(failed)
|
||||
remroles = discord_user.roles
|
||||
else:
|
||||
# Superadmin
|
||||
if access_level["level"] in [10, 20]:
|
||||
if self.get_role(self.discord_roles.admin, discord_user.roles) is None:
|
||||
rank = self.get_role(self.discord_roles.admin, self.guild.roles)
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.admin, discord_user.roles)
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
# Member Role
|
||||
if 100 > access_level["level"]:
|
||||
rank = self.get_role(self.discord_roles.member, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.member, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
# Leader
|
||||
if self.account_service.check_leader(account.main):
|
||||
rank = self.get_role(self.discord_roles.leader, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.leader, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
if self.setting_service.get_value("is_alliance_bot") == "1":
|
||||
|
||||
if self.account_service.check_council(account.main):
|
||||
rank = self.get_role(self.discord_roles.council, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.council, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
# President
|
||||
if self.account_service.check_president(account.main):
|
||||
rank = self.get_role(self.discord_roles.president, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.president, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
# General
|
||||
if self.account_service.check_general(account.main):
|
||||
rank = self.get_role(self.discord_roles.general, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.general, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
# Officer
|
||||
if self.account_service.check_officer(account.main):
|
||||
rank = self.get_role(self.discord_roles.officer, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
addroles.append(rank)
|
||||
else:
|
||||
rank = self.get_role(self.discord_roles.officer, self.guild.roles) or None
|
||||
if rank is not None:
|
||||
remroles.append(rank)
|
||||
|
||||
if account.disabled == 1 or access_level == 0 or access_level == 100:
|
||||
addroles = [self.get_role(self.discord_roles.failed, self.guild.roles)]
|
||||
remroles = discord_user.roles
|
||||
else:
|
||||
remroles.append(self.get_role(self.discord_roles.failed, self.guild.roles))
|
||||
# Execute
|
||||
if len(addroles) > 0:
|
||||
addroles = list(dict.fromkeys(addroles)) # Remove Duplicates
|
||||
for role in addroles:
|
||||
await discord_user.add_roles(role)
|
||||
if len(remroles) > 0:
|
||||
remroles = list(dict.fromkeys(remroles)) # Remove Duplicates
|
||||
for role in remroles:
|
||||
if role.name == "@everyone": # Skip @everyone as we cant remove that
|
||||
continue
|
||||
await discord_user.remove_roles(role)
|
||||
|
||||
async def on_message(self, msg: Message):
|
||||
if msg.author.id == self.client.user.id:
|
||||
return
|
||||
channel: TextChannel = msg.channel
|
||||
if channel.id == int(self.setting_service.get_value("dc_relay_public")):
|
||||
if self.setting_service.get_value('is_alliance_bot') == "0":
|
||||
response = f"[<notice>{html.escape(msg.author.nick if msg.author.nick else msg.author.name, False)}" \
|
||||
f"</notice>]: " \
|
||||
f"{html.escape(emojis.decode(msg.clean_content), False)}"
|
||||
else:
|
||||
response = f"{html.escape(msg.author.nick if msg.author.nick else msg.author.name, False)}: " \
|
||||
f"{html.escape(emojis.decode(msg.clean_content), False)}"
|
||||
await msg.delete(delay=3600)
|
||||
self.relay_hub_service.send_message("public_relay", [msg.author.nick, msg.author], response, response)
|
||||
|
||||
if self.is_command(msg.content):
|
||||
admin = self.get_role("Administrator", self.guild.roles)
|
||||
council = self.get_role("Council", self.guild.roles)
|
||||
|
||||
if msg.content[:4] == "!pin":
|
||||
if admin in msg.author.roles:
|
||||
matches = re.findall(pattern="<head>(.+?)<\/head>(.+)", string=msg.content[4:].strip(),
|
||||
flags=re.DOTALL)
|
||||
if matches:
|
||||
mess = await channel.send(
|
||||
embed=Embed(color=3066993, title=matches[0][0], description=matches[0][1]))
|
||||
else:
|
||||
mess = await channel.send(embed=Embed(color=3066993, description=msg.content[4:]))
|
||||
await mess.pin()
|
||||
|
||||
if msg.content[:5] == "!note":
|
||||
if admin in msg.author.roles:
|
||||
matches = re.findall(pattern="<head>(.+?)<\/head>(.+)", string=msg.content[5:].strip(),
|
||||
flags=re.DOTALL)
|
||||
if matches:
|
||||
mess = await channel.send(
|
||||
embed=Embed(color=3066993, title=matches[0][0], description=matches[0][1]))
|
||||
else:
|
||||
mess = await channel.send(embed=Embed(color=3066993, description=msg.content[5:]))
|
||||
|
||||
if msg.content[:6] == "!purge":
|
||||
if admin in msg.author.roles:
|
||||
count: str = msg.content[6:].strip()
|
||||
if type(count) == str:
|
||||
if count.isdigit():
|
||||
# noinspection PyTypeChecker
|
||||
count = int(count)
|
||||
if type(count) == int:
|
||||
# noinspection PyTypeChecker
|
||||
while count > 100:
|
||||
await msg.channel.purge(check=self.check)
|
||||
count -= 100
|
||||
else:
|
||||
await msg.channel.purge(check=self.check, limit=count)
|
||||
await msg.delete(delay=0)
|
||||
if msg.content[:10] == "!subscribe":
|
||||
if admin in msg.author.roles:
|
||||
out = msg.content[10:].strip()
|
||||
if self.setting_service.get(out) is None:
|
||||
await msg.reply(f"The Channel {out} does not exist.")
|
||||
else:
|
||||
self.setting_service.set_value(out, msg.channel.id)
|
||||
self.send_message(msg.channel.id, Embed(color=3066993, title="Channel Guard",
|
||||
description=f"This channel has been "
|
||||
f"subscriped to the source {out}."))
|
||||
|
||||
def send_message(self, channel: int, message, delete=0):
|
||||
if self.client.is_ready():
|
||||
channel: TextChannel = self.client.get_channel(int(channel)) if type(channel) in [int, str] else channel
|
||||
if type(message) == Embed:
|
||||
if delete != 0:
|
||||
asyncio.run_coroutine_threadsafe(channel.send(embed=message, delete_after=delete), self.loop)
|
||||
else:
|
||||
asyncio.run_coroutine_threadsafe(channel.send(embed=message), self.loop)
|
||||
|
||||
else:
|
||||
if delete != 0:
|
||||
asyncio.run_coroutine_threadsafe(channel.send(message, delete_after=delete), self.loop)
|
||||
else:
|
||||
asyncio.run_coroutine_threadsafe(channel.send(message), self.loop)
|
||||
|
||||
def is_command(self, message: str):
|
||||
for x in ["subscribe", "pin", "purge", "note"]:
|
||||
if message.startswith("!" + x):
|
||||
return True
|
||||
|
||||
async def clean_channel(self):
|
||||
if self.setting_service.get_value("dc_relay_public") not in ["0", None]:
|
||||
channel: TextChannel = self.client.get_channel(int(self.setting_service.get_value("dc_relay_public")))
|
||||
for i in range(5):
|
||||
await channel.purge(check=self.check)
|
||||
|
||||
def check(self, msg: Message):
|
||||
if msg.pinned or len(msg.embeds) > 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@event(event_type="main_changed", description="Fix discord names & ranks")
|
||||
def fix_ranks(self, _, data):
|
||||
row = self.db.query_single("SELECT * from account where char_id=?", [data.old_main_id])
|
||||
if row.discord_joined == 0:
|
||||
self.logger.debug(f"{data.old_main_id} was not in discord, ignoring main fixing")
|
||||
return
|
||||
elif row.discord_joined == 1:
|
||||
self.db.exec(
|
||||
"UPDATE account set discord_handle=?, discord_id=?, discord_invite=?, discord_joined=? where char_id=?",
|
||||
[row.discord_handle, row.discord_id, row.discord_invite, row.discord_joined, data.new_main_id])
|
||||
self.db.exec(
|
||||
"UPDATE account set discord_handle='', discord_id=0, discord_invite='', discord_joined=0 "
|
||||
"where char_id=?",
|
||||
[data.old_main_id])
|
||||
self.logger.info(f"{data.old_main_id} was in discord, overwriting {data.new_main_id} with {data}")
|
||||
|
||||
self.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 = discord.Activity(type=discord.ActivityType.listening,
|
||||
name=f"{count} Players")
|
||||
asyncio.run_coroutine_threadsafe(self.client.change_presence(activity=act), self.loop)
|
||||
@@ -0,0 +1,30 @@
|
||||
Our Home: <a href='chatcmd:///start https://www.aoalliance.org/'>https://www.aoalliance.org/</a>
|
||||
Gitlab Repository: <a href='chatcmd:///start https://gitlab.com/CynderGames/igncore'>https://gitlab.com/CynderGames/igncore</a>
|
||||
|
||||
IGNCore is a performance oriented fork of Tyrbot based on version 0.5,
|
||||
and is being maintained by a group <highlight>Volunteers</highlight>.
|
||||
|
||||
At this point, we'd like to send a special "thank you" to the following people, which in one way or another, contributed to IGNCore/IGNCom:
|
||||
» <highlight>Zetabyte & its Network</highlight>, for many feature ideas,
|
||||
and the heavy assistance with knowledge.
|
||||
» <highlight>Chrisax</highlight> for hosting the !history mirror.
|
||||
» Everyone who participated in test runs,
|
||||
» sent us ideas for bot improvements,
|
||||
» helped us by sending bug reports our way,
|
||||
» or just by keeping us busy during a long night of development.
|
||||
|
||||
aswell as the initial developers of Tyrbot, on which IGNCore is based:
|
||||
» <highlight>Tyrence</highlight>
|
||||
» <highlight>Teeko</highlight> for all the time he spent testing new features,
|
||||
reporting bugs, and making suggestions
|
||||
» <highlight>nepherius/wafflespower</highlight> who was critical during the
|
||||
early parts of development in testing and suggesting changes
|
||||
» <highlight>minidodo</highlight> for his significant contributions and fixes
|
||||
to Tyrbot, and his willingness to assist other users in the Discord channel
|
||||
» <highlight>hughp135</highlight>
|
||||
» <highlight>jroovers</highlight>
|
||||
» <highlight>dustify</highlight>
|
||||
» <highlight>equinitry</highlight>
|
||||
» <highlight>Ilon Sjögren</highlight>
|
||||
|
||||
If you'd like to know more, feel free to contact us on the toons listed in <highlight>!admins</highlight> - Administrators.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"no_help": {
|
||||
"en_US": "Could not find help on <highlight>{topic}</highlight>",
|
||||
"de_DE": "Zu dem Thema <highlight>{topic}</highlight> konnte keine Hilfe gefunden werden."
|
||||
},
|
||||
"about_title": {
|
||||
"en_US": "About {version}",
|
||||
"de_DE": "Über <highlight>{version}</highlight>"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import os
|
||||
|
||||
import hjson
|
||||
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Any, NamedFlagParameters
|
||||
from core.decorators import instance, command
|
||||
from core.translation_service import TranslationService
|
||||
|
||||
|
||||
@instance()
|
||||
class HelpController:
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.text = registry.get_instance("text")
|
||||
self.db = registry.get_instance("db")
|
||||
self.access_service = registry.get_instance("access_service")
|
||||
self.command_service = registry.get_instance("command_service")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
|
||||
def start(self):
|
||||
self.ts.register_translation("module/help", self.load_help_msg)
|
||||
self.command_alias_service.add_alias("version", "about")
|
||||
|
||||
def load_help_msg(self):
|
||||
with open("modules/core/help/help.msg", mode="r", encoding="UTF-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
@command(command="about", params=[], access_level="member",
|
||||
description="Show information about the development of this bot")
|
||||
def about_cmd(self, _):
|
||||
with open(os.path.dirname(os.path.realpath(__file__)) + os.sep + "about.txt", mode="r", encoding="UTF-8") as f:
|
||||
return ChatBlob(self.getresp("module/help", "about_title",
|
||||
{"version": f"{self.bot.major_version}.{self.bot.minor_version}"}), f.read())
|
||||
|
||||
@command(command="help", params=[], access_level="member",
|
||||
description="Show a list of commands to get help with")
|
||||
def help_list_cmd(self, request):
|
||||
data = self.db.query("SELECT command, module, access_level FROM command_config "
|
||||
"WHERE enabled = 1 "
|
||||
"ORDER BY module, command")
|
||||
blob = ""
|
||||
current_group = ""
|
||||
current_module = ""
|
||||
current_command = ""
|
||||
access_level = self.access_service.get_access_level(request.sender.char_id)
|
||||
for row in data:
|
||||
if access_level["level"] > self.access_service.get_access_level_by_label(row.access_level)["level"]:
|
||||
continue
|
||||
|
||||
parts = row.module.split(".")
|
||||
group = parts[0]
|
||||
module = parts[1]
|
||||
if group != current_group:
|
||||
current_group = group
|
||||
blob += "\n\n<header2>" + current_group + "</header2>"
|
||||
|
||||
if module != current_module:
|
||||
current_module = module
|
||||
blob += "\n" + module + ":"
|
||||
|
||||
if row.command != current_command:
|
||||
current_command = row.command
|
||||
blob += " " + self.text.make_tellcmd(row.command, "help " + row.command)
|
||||
|
||||
return ChatBlob("Help (main)", blob)
|
||||
|
||||
@command(command="help", params=[Any("command"), NamedFlagParameters(["show_regex"])], access_level="member",
|
||||
description="Show help for a specific command")
|
||||
def help_detail_cmd(self, request, help_topic, named_params):
|
||||
help_topic = help_topic.lower()
|
||||
|
||||
# check for alias
|
||||
alias = self.command_alias_service.check_for_alias(help_topic)
|
||||
if alias:
|
||||
help_topic = alias
|
||||
|
||||
help_text = self.command_service.get_help_text(request.sender.char_id, help_topic,
|
||||
request.channel, named_params.show_regex)
|
||||
if help_text:
|
||||
return self.command_service.format_help_text(help_topic, help_text)
|
||||
else:
|
||||
return self.getresp("module/help", "no_help", {"topic": help_topic})
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"invite_fail": {
|
||||
"en_US": "<highlight>{target}</highlight> is already in the private channel.",
|
||||
"de_DE": "Der Spieler <highlight>{target}</highlight> ist bereits ein MItglied des privaten Channels."
|
||||
},
|
||||
"invite_success_target": {
|
||||
"en_US": "You have been invited to the private channel by <highlight>{inviter}</highlight>.",
|
||||
"de_DE": "Du wurdest von <highlight>{inviter}</highlight> zu meinem privaten Channel eingeladen."
|
||||
},
|
||||
"invite_success_self": {
|
||||
"en_US": "You have invited <highlight>{target}</highlight> to the private channel.",
|
||||
"de_DE": "Du hast <highlight>{target}</highlight> erfolgreich in den privaten Channel eingeladen."
|
||||
},
|
||||
"kick_success_target": {
|
||||
"en_US": "You have been kicked from the private channel by <highlight>{kicker}</highlight>.",
|
||||
"de_DE": "Du wurdest von <highlight>{kicker}</highlight> aus dem privaten Channel gekickt."
|
||||
},
|
||||
"kick_success_self": {
|
||||
"en_US": "You have kicked <highlight>{target}</highlight> from the private channel.",
|
||||
"de_DE": "Du hast <highlight>{target}</highlight> aus dem privaten Channel gekickt."
|
||||
},
|
||||
"kick_fail": {
|
||||
"en_US": "You do not have the required access level to kick <highlight>{target}</highlight>.",
|
||||
"de_DE": "Du hast nicht das benötigte Zugriffslevel, um <highlight>{target}</highlight> zu kicken."
|
||||
},
|
||||
"kick_fail_not_in_priv": {
|
||||
"en_US": "<highlight>{target}</highlight> is not an member of the private channel.",
|
||||
"de_DE": "<highlight>{target}</highlight> ist kein Mitglied des privaten Channels."
|
||||
},
|
||||
"join": {
|
||||
"en_US": "{char} has joined the private channel. {logon}",
|
||||
"de_DE": "<orange>Online<end>: {char}. {logon}"
|
||||
},
|
||||
"leave": {
|
||||
"en_US": "<highlight>{char}</highlight> has left the private channel. {logoff}",
|
||||
"de_DE": "<highlight>{char}</highlight> hat <yellow>sich nach</yellow> <red>Terra</red> <blue>gebeamt.</blue> {logoff}"
|
||||
},
|
||||
"kick_all": {
|
||||
"en_US": "Everyone will be kicked from this channel in 10 seconds. [by <highlight>{char}</highlight>]",
|
||||
"de_DE": "In 10 Sekunden wird jeder aus dem Channel gekickt. [von <highlight>{char}</highlight>]"
|
||||
},
|
||||
"mem_add_fail": {
|
||||
"en_US": "<highlight>{char}</highlight> is already a member.",
|
||||
"de_DE": "<highlight>{char}</highlight> ist bereits ein Mitglied."
|
||||
},
|
||||
"mem_add_success": {
|
||||
"en_US": "<highlight>{char}</highlight> has been added as a member.",
|
||||
"de_DE": "<highlight>{char}</highlight> wurde als Mitglied hinzugefügt."
|
||||
},
|
||||
"mem_rem_success": {
|
||||
"en_US": "<highlight>{char}</highlight> has been removed as a member.",
|
||||
"de_DE": "<highlight>{char}</highlight> wurde als Mitglied entfernt."
|
||||
},
|
||||
"mem_rem_fail": {
|
||||
"en_US": "<highlight>{char}</highlight> is not a member.",
|
||||
"de_DE": "<highlight>{char}</highlight> ist kein Mitglied."
|
||||
},
|
||||
"blob_mem_list": {
|
||||
"en_US": "Members ({amount})",
|
||||
"de_DE": "Mitglieder ({amount})"
|
||||
},
|
||||
"autoinvite_changed": {
|
||||
"en_US": "Your auto invite preference has been set to <highlight>{changedto}</highlight>.",
|
||||
"de_DE": "Deine Autoinvite Einstellung ist nun <highlight>{changedto}</highlight>."
|
||||
},
|
||||
"not_an_member": {
|
||||
"en_US": "You must be a member of this bot to set your auto invite preference.",
|
||||
"de_DE": "Du musst ein Mitglied des Bots sein, um deine Autoinvite Einstellung zu setzen."
|
||||
},
|
||||
"auto_invited": {
|
||||
"en_US": "You have been auto-invited to the private channel.",
|
||||
"de_DE": "Du wurdest automatsich in meinen privaten Channel eingeladen."
|
||||
},
|
||||
"on": {
|
||||
"en_US": "on",
|
||||
"de_DE": "an"
|
||||
},
|
||||
"off": {
|
||||
"en_US": "off",
|
||||
"de_DE": "aus"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
import hjson
|
||||
|
||||
from core.buddy_service import BuddyService
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Character, Multiple
|
||||
from core.db import DB, SqlException
|
||||
from core.decorators import instance, command, event
|
||||
from core.dict_object import DictObject
|
||||
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.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
from core.tyrbot import Tyrbot
|
||||
from core.util import Util
|
||||
from modules.core.accounting.services.account_service import AccountService
|
||||
from modules.core.ban.ban_service import BanService
|
||||
from modules.standard.online.online_display import OnlineDisplay
|
||||
|
||||
|
||||
@instance()
|
||||
class PrivateChannelController:
|
||||
MESSAGE_SOURCE = "private_channel"
|
||||
PRIVATE_CHANNEL_PREFIX = "[<cyan>Priv</cyan>] "
|
||||
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.util: Util = registry.get_instance("util")
|
||||
self.private_channel_service = registry.get_instance("private_channel_service")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.pork: PorkService = registry.get_instance("pork_service")
|
||||
self.job_scheduler = registry.get_instance("job_scheduler")
|
||||
self.access_service = registry.get_instance("access_service")
|
||||
self.message_hub_service = registry.get_instance("message_hub_service")
|
||||
self.ban_service = registry.get_instance("ban_service")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
|
||||
self.account_service: AccountService = registry.get_instance("account_service")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.priv: PrivateChannelService = registry.get_instance("private_channel_service")
|
||||
|
||||
def pre_start(self):
|
||||
self.db.create_view("online")
|
||||
self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)
|
||||
|
||||
try:
|
||||
self.reinvite = [x.char_id for x in
|
||||
self.db.query("SELECT char_id from online where channel = ?", [self.bot.name])]
|
||||
|
||||
except SqlException:
|
||||
self.reinvite = []
|
||||
|
||||
def start(self):
|
||||
self.message_hub_service.register_message_destination(self.MESSAGE_SOURCE,
|
||||
self.handle_incoming_relay_message,
|
||||
["registration"],
|
||||
[self.MESSAGE_SOURCE])
|
||||
self.ts.register_translation("module/private_channel", self.load_private_channel_msg)
|
||||
|
||||
@event("connect", "Reinvite previous raiders")
|
||||
def reinvite_all(self, _, _1):
|
||||
for user in self.reinvite:
|
||||
self.bot.send_mass_message(user, "You have been <green>reinvited</green> into my private channel "
|
||||
"after a bot restart or crash; Sorry for the inconvenience.")
|
||||
self.priv.invite(user)
|
||||
del self.reinvite
|
||||
|
||||
@staticmethod
|
||||
def load_private_channel_msg():
|
||||
with open("modules/core/private_channel/private_channel.msg", mode="r", encoding="utf-8") as f:
|
||||
return hjson.load(f)
|
||||
|
||||
def handle_incoming_relay_message(self, ctx):
|
||||
self.bot.send_private_channel_message(ctx.formatted_message, fire_outgoing_event=False)
|
||||
|
||||
@event(event_type="member_logon", description="Send autoinvites to players logging in")
|
||||
def logon_event(self, _, data):
|
||||
if not self.bot.is_ready():
|
||||
if data.packet.char_id not in self.reinvite:
|
||||
account = data.account
|
||||
if account.disabled == 1:
|
||||
pass
|
||||
elif account.auto_invite == 1:
|
||||
self.reinvite.append(data.packet.char_id)
|
||||
return
|
||||
if self.private_channel_service.in_private_channel(data.packet.char_id):
|
||||
return
|
||||
account = data.account
|
||||
if account.disabled == 1:
|
||||
return
|
||||
if self.db.query_single("SELECT * from org_bots where char_id=?", [data.packet.char_id]):
|
||||
return
|
||||
if account.auto_invite == 1:
|
||||
if self.pork.get_character_info(data.packet.char_id).org_id != self.bot.public_channel_service.org_id:
|
||||
self.private_channel_service.invite(data.packet.char_id)
|
||||
self.bot.send_mass_message(data.packet.char_id, "You have been "
|
||||
"<highlight>auto invited</highlight> "
|
||||
"into my private channel.")
|
||||
|
||||
@command(command="join", params=[], access_level="member",
|
||||
description="Join the private channel")
|
||||
def join_cmd(self, request):
|
||||
self.private_channel_service.invite(request.sender.char_id)
|
||||
|
||||
@command(command="leave", params=[], access_level="member",
|
||||
description="Leave the private channel")
|
||||
def leave_cmd(self, request):
|
||||
self.private_channel_service.kick(request.sender.char_id)
|
||||
|
||||
@command(command="invite", params=[Multiple(Character("character"))], access_level="member",
|
||||
description="Invite a character to the private channel")
|
||||
def invite_cmd(self, request, chars):
|
||||
success, in_channel, none, banned = [], [], [], []
|
||||
for char in chars:
|
||||
if char.char_id:
|
||||
if self.private_channel_service.in_private_channel(char.char_id):
|
||||
in_channel.append(char.name)
|
||||
elif self.ban_service.get_ban(char.char_id):
|
||||
banned.append(char.name)
|
||||
else:
|
||||
self.bot.send_private_message(char.char_id, self.getresp("module/private_channel",
|
||||
"invite_success_target",
|
||||
{"inviter": request.sender.name}))
|
||||
self.private_channel_service.invite(char.char_id)
|
||||
success.append(char.name)
|
||||
else:
|
||||
none.append(char.name)
|
||||
out = ""
|
||||
if len(in_channel) > 0:
|
||||
out += self.getresp("module/private_channel", "invite_fail", {"target": ", ".join(in_channel)}) + "\n"
|
||||
if len(success) > 0:
|
||||
out += self.getresp("module/private_channel", "invite_success_self", {"target": ", ".join(success)}) + "\n"
|
||||
if len(none) > 0:
|
||||
out += self.getresp("global", "char_not_found", {"char": ", ".join(none)}) + "\n"
|
||||
if len(banned) > 0:
|
||||
out += f'The Character <highlight>{", ".join(banned)}</highlight> is <red>banned</red>, ' \
|
||||
f'and cannot be invited.'
|
||||
if request.channel == "priv":
|
||||
return out.strip("\n")
|
||||
else:
|
||||
self.bot.send_mass_message(request.sender.char_id, out.strip("\n"))
|
||||
|
||||
@command(command="kick", params=[Character("character")], access_level="moderator",
|
||||
description="Kick a character from the private channel")
|
||||
def kick_cmd(self, request, char):
|
||||
if char.char_id:
|
||||
if not self.private_channel_service.in_private_channel(char.char_id):
|
||||
return self.getresp("module/private_channel", "kick_fail_not_in_priv", {"target": char.name})
|
||||
else:
|
||||
if self.access_service.has_sufficient_access_level(request.sender.char_id, char.char_id):
|
||||
self.bot.send_private_message(char.char_id, self.getresp("module/private_channel",
|
||||
"kick_success_target",
|
||||
{"kicker": request.sender.name}))
|
||||
self.private_channel_service.kick(char.char_id)
|
||||
return self.getresp("module/private_channel", "kick_success_self", {"target": char.name})
|
||||
else:
|
||||
return self.getresp("module/private_channel", "kick_fail", {"target": char.name})
|
||||
else:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
|
||||
@command(command="kickall", params=[], access_level="moderator",
|
||||
description="Kick all characters from the private channel")
|
||||
def kickall_cmd(self, request):
|
||||
self.bot.send_private_channel_message(self.getresp("module/private_channel", "kick_all",
|
||||
{"char": request.sender.name}))
|
||||
self.job_scheduler.delayed_job(lambda t: self.private_channel_service.kickall(), 10)
|
||||
|
||||
@event(event_type=BanService.BAN_ADDED_EVENT, description="Kick characters from the private channel who are banned",
|
||||
is_hidden=True)
|
||||
def ban_added_event(self, _, event_data):
|
||||
self.private_channel_service.kick(event_data.char_id)
|
||||
|
||||
@event(event_type=PrivateChannelService.PRIVATE_CHANNEL_MESSAGE_EVENT,
|
||||
description="Relay messages from the private channel to the relay hub", is_hidden=True)
|
||||
def handle_private_channel_message_event(self, _, event_data):
|
||||
if event_data.char_id == self.bot.get_char_id() or self.ban_service.get_ban(event_data.char_id):
|
||||
return
|
||||
|
||||
char_name = self.character_service.resolve_char_to_name(event_data.char_id)
|
||||
sender = DictObject({"char_id": event_data.char_id, "name": char_name})
|
||||
char = self.text.make_charlink(char_name)
|
||||
formatted_message = f"{self.PRIVATE_CHANNEL_PREFIX} {char}: {event_data.message}"
|
||||
self.message_hub_service.send_message(self.MESSAGE_SOURCE, sender, event_data.message, formatted_message)
|
||||
|
||||
@event(event_type=PrivateChannelService.JOINED_PRIVATE_CHANNEL_EVENT,
|
||||
description="Notify when a character joins the private channel")
|
||||
def handle_private_channel_joined_event(self, _, event_data):
|
||||
main = self.account_service.get_account(event_data.char_id)
|
||||
if main:
|
||||
info = "" if self.account_service.check_superadmin(main.char_id) else \
|
||||
":: <red>Admin</red> " if self.account_service.check_admin(main.char_id) else \
|
||||
":: <red>Moderator</red> " if self.account_service.check_moderator(main.char_id) else \
|
||||
":: <orange>Raidleader</orange> " if self.account_service.check_leader(main.char_id) else ""
|
||||
if main.char_id != event_data.char_id:
|
||||
info += f":: Alt of <{main.faction.lower()}>{main.name}</{main.faction.lower()}>"
|
||||
else:
|
||||
if self.setting_service.get_value('is_alliance_bot') == "0":
|
||||
info = ":: <red>WARN</red> » <notice>NO ACC</notice>"
|
||||
else:
|
||||
info = ""
|
||||
|
||||
msg = f"{self.text.format_char_info(self.pork.get_character_info(event_data.char_id))} joined us {info}"
|
||||
self.bot.send_private_channel_message(msg, fire_outgoing_event=False)
|
||||
self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, msg, self.PRIVATE_CHANNEL_PREFIX + msg)
|
||||
od = OnlineDisplay(self.text, self.util, self.db)
|
||||
params = [self.bot.name, self.bot.get_char_id()]
|
||||
self.bot.send_mass_message(event_data.char_id,
|
||||
od.format_blob(od.format_by_channel_prof("and channel_id IN (1, 2) ", params)))
|
||||
|
||||
@event(event_type=PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT,
|
||||
description="Notify when a character leaves the private channel")
|
||||
def handle_private_channel_left_event(self, _, event_data):
|
||||
char_info = self.pork.get_character_info(event_data.char_id)
|
||||
msg = f"<{char_info.faction.lower()}>{char_info.name}</{char_info.faction.lower()}> left us"
|
||||
self.bot.send_private_channel_message(msg, fire_outgoing_event=False)
|
||||
self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, msg, msg)
|
||||
|
||||
@event(event_type=Tyrbot.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT,
|
||||
description="Relay commands from the private channel to the relay hub", is_hidden=True)
|
||||
def outgoing_private_channel_message_event(self, _, event_data):
|
||||
if isinstance(event_data.message, ChatBlob):
|
||||
pages = self.text.paginate(ChatBlob(event_data.message.title, event_data.message.msg),
|
||||
self.setting_service.get("org_channel_max_page_length").get_value())
|
||||
if len(pages) < 4:
|
||||
for page in pages:
|
||||
message = "{priv} {message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=page)
|
||||
self.message_hub_service.send_message(self.MESSAGE_SOURCE,
|
||||
None,
|
||||
page,
|
||||
message)
|
||||
else:
|
||||
message = "{priv} {message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message.title)
|
||||
self.message_hub_service.send_message(self.MESSAGE_SOURCE,
|
||||
None,
|
||||
event_data.message.title,
|
||||
message)
|
||||
else:
|
||||
message = "{priv} {message}".format(priv=self.PRIVATE_CHANNEL_PREFIX, message=event_data.message)
|
||||
self.message_hub_service.send_message(self.MESSAGE_SOURCE,
|
||||
None,
|
||||
message,
|
||||
message)
|
||||
@@ -0,0 +1,75 @@
|
||||
import time
|
||||
|
||||
from core.command_param_types import Const, Any, Options
|
||||
from core.db import DB
|
||||
from core.decorators import instance, command, setting, event
|
||||
from core.dict_object import DictObject
|
||||
from core.private_channel_service import PrivateChannelService
|
||||
from core.setting_types import DictionarySettingType
|
||||
from core.text import Text
|
||||
|
||||
|
||||
@instance()
|
||||
class TopicController:
|
||||
def inject(self, registry):
|
||||
self.bot = registry.get_instance("bot")
|
||||
self.db: DB = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.util = registry.get_instance("util")
|
||||
self.command_alias_service = registry.get_instance("command_alias_service")
|
||||
self.private_channel_service: PrivateChannelService = registry.get_instance("private_channel_service")
|
||||
|
||||
def start(self):
|
||||
self.command_alias_service.add_alias("motd", "topic")
|
||||
|
||||
@setting(name="topic", value="", description="The bot topic")
|
||||
def topic(self):
|
||||
return DictionarySettingType()
|
||||
|
||||
@command(command="topic", params=[], access_level="member",
|
||||
description="Show the current topic")
|
||||
def topic_show_command(self, request):
|
||||
topic = self.topic().get_value()
|
||||
if topic:
|
||||
return self.format_topic_message(topic)
|
||||
else:
|
||||
return "There is no current topic."
|
||||
|
||||
@command(command="topic", params=[Options(["clear", "unset"])], access_level="leader",
|
||||
description="Clears the current topic", sub_command="modify")
|
||||
def topic_clear_command(self, _, _1):
|
||||
self.topic().set_value("")
|
||||
|
||||
return "The topic has been cleared."
|
||||
|
||||
@command(command="topic",
|
||||
params=[Const("set", is_optional=True), Any("topic_message")],
|
||||
access_level="leader",
|
||||
description="Set the current topic", sub_command="modify")
|
||||
def topic_set_command(self, request, _, topic_message):
|
||||
sender = DictObject({"name": request.sender.name, "char_id": request.sender.char_id})
|
||||
|
||||
topic = {"topic_message": topic_message,
|
||||
"created_by": sender,
|
||||
"created_at": int(time.time())}
|
||||
|
||||
self.topic().set_value(topic)
|
||||
|
||||
return "The topic has been set."
|
||||
|
||||
def format_topic_message(self, topic):
|
||||
time_string = self.util.time_to_readable(int(time.time()) - topic["created_at"])
|
||||
return f"Topic: <highlight>{topic['topic_message']}<end> " \
|
||||
f"[set by <highlight>{topic['created_by']['name']}<end>][{time_string} ago]"
|
||||
|
||||
@event(PrivateChannelService.JOINED_PRIVATE_CHANNEL_EVENT, "Show topic to characters joining the private channel")
|
||||
def show_topic(self, _, event_data):
|
||||
topic = self.topic().get_value()
|
||||
if topic:
|
||||
self.bot.send_private_message(event_data.char_id, self.format_topic_message(topic))
|
||||
|
||||
@event(PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT,
|
||||
"Clear topic when there are no characters in the private channel")
|
||||
def clear_topic(self, _, event_data):
|
||||
if self.topic().get_value() and len(self.private_channel_service.get_all_in_private_channel()) == 0:
|
||||
self.topic().set_value("")
|
||||
@@ -0,0 +1,395 @@
|
||||
import math
|
||||
|
||||
from core.buddy_service import BuddyService
|
||||
from core.chat_blob import ChatBlob
|
||||
from core.command_param_types import Const, Character, Any, NamedParameters
|
||||
from core.decorators import instance, command, event
|
||||
from core.dict_object import DictObject
|
||||
from core.logger import Logger
|
||||
from core.lookup.character_service import CharacterService
|
||||
from core.lookup.pork_service import PorkService
|
||||
from core.private_channel_service import PrivateChannelService
|
||||
from core.setting_service import SettingService
|
||||
from core.setting_types import TextSettingType
|
||||
from core.text import Text
|
||||
from core.translation_service import TranslationService
|
||||
from core.tyrbot import Tyrbot
|
||||
|
||||
|
||||
# noinspection SqlCaseVsIf,SqlCaseVsCoalesce
|
||||
@instance()
|
||||
class RIAdminController:
|
||||
PAGE_SIZE = 20
|
||||
UNASSIGNED_RAID_INSTANCE_ID = 0
|
||||
|
||||
def __init__(self):
|
||||
self.logger = Logger(__name__)
|
||||
|
||||
def inject(self, registry):
|
||||
self.bot: Tyrbot = registry.get_instance("bot")
|
||||
self.db = registry.get_instance("db")
|
||||
self.text: Text = registry.get_instance("text")
|
||||
self.setting_service: SettingService = registry.get_instance("setting_service")
|
||||
self.util = registry.get_instance("util")
|
||||
self.character_service: CharacterService = registry.get_instance("character_service")
|
||||
self.private_channel_service: PrivateChannelService = registry.get_instance("private_channel_service")
|
||||
self.ts: TranslationService = registry.get_instance("translation_service")
|
||||
self.getresp = self.ts.get_response
|
||||
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
|
||||
self.pork: PorkService = registry.get_instance("pork_service")
|
||||
|
||||
def start(self):
|
||||
self.db.exec(
|
||||
"CREATE TABLE IF NOT EXISTS raid_instance (id INT PRIMARY KEY AUTO_INCREMENT, "
|
||||
"name VARCHAR(255) NOT NULL, "
|
||||
"bot varchar(32)) ENGINE MEMORY")
|
||||
self.db.exec(
|
||||
"CREATE TABLE IF NOT EXISTS raid_instance_char ("
|
||||
"raid_instance_id INT NOT NULL, "
|
||||
"char_id INT PRIMARY KEY, "
|
||||
"is_leader TINYINT NOT NULL) ENGINE MEMORY")
|
||||
self.setting_service.register_new(self.module_name, "riadmin_network",
|
||||
"[905848882, 272234, 1923370, 313107, 1217210821, 1134420908]",
|
||||
TextSettingType(), "Allowed bots (charID's)",
|
||||
extended_description="This setting is *NOT* synchronized across the network;"
|
||||
" this needs to be done manually!")
|
||||
|
||||
@event(event_type="buddy_logoff", description="Track raiders")
|
||||
def raider_logoff(self, _, event_data):
|
||||
if self.bot.is_ready():
|
||||
buddy = self.buddy_service.get_buddy(event_data.char_id)
|
||||
if not buddy:
|
||||
return
|
||||
if "raider" in buddy.get('types', []):
|
||||
user = self.db.query_single("SELECT * from player where char_id=?", [event_data.char_id])
|
||||
raid = self.get_raid_instance_by_char(event_data.char_id)
|
||||
if user and raid:
|
||||
if raid.bot != 0:
|
||||
self.bot.send_private_channel_message(f'[<red>RI</red>] Raider logged off: '
|
||||
f'{user.name} - {user.profession} [{raid.name}]')
|
||||
self.db.exec("DELETE FROM raid_instance_char WHERE char_id = ?", [event_data.char_id])
|
||||
self.buddy_service.remove_buddy(event_data.char_id, 'raider')
|
||||
|
||||
@event(event_type=PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, description="Track Raiders")
|
||||
def raider_leave(self, _, event_data):
|
||||
if self.bot.is_ready():
|
||||
buddy = self.buddy_service.get_buddy(event_data.char_id)
|
||||
if not buddy:
|
||||
return
|
||||
if "raider" in buddy.get('types', []):
|
||||
user = self.pork.get_character_info(event_data.char_id)
|
||||
raid = self.get_raid_instance_by_char(event_data.char_id)
|
||||
part = int(raid.bot) if raid else 0
|
||||
if part in [self.bot.get_char_id(), 0]:
|
||||
if part != 0:
|
||||
self.bot.send_private_channel_message(
|
||||
f'[<red>RI</red>] Raider left: {user.name} - {user.profession} '
|
||||
f'[{raid.name if raid else "UNASSIGNED"}]')
|
||||
self.db.exec("DELETE FROM raid_instance_char WHERE char_id = ?", [event_data.char_id])
|
||||
self.buddy_service.remove_buddy(event_data.char_id, 'raider')
|
||||
|
||||
@event(event_type="connect", description="Init raider tracking on startup")
|
||||
def connect(self, _, _1):
|
||||
for user in self.get_raid_instance_chars():
|
||||
self.buddy_service.add_buddy(user.char_id, 'raider')
|
||||
|
||||
@command(command="riadmin", params=[NamedParameters(['page'])], access_level="leader",
|
||||
description="Show the current RIs")
|
||||
def riadmin(self, _, named_params):
|
||||
refresh = self.text.make_tellcmd("Refresh", "riadmin")
|
||||
send = self.text.make_tellcmd("Apply", "riadmin send")
|
||||
clear = self.text.make_tellcmd("Clear All", "riadmin clear")
|
||||
blob = f"[{refresh}] - Update the RI List\n" \
|
||||
f"[{send}] - Send all RI's to their bots\n" \
|
||||
f"[{clear}] - Clear all RI's, and delete them"
|
||||
blob += "\n\n"
|
||||
|
||||
page = int(named_params.page or "1")
|
||||
offset = (page - 1) * self.PAGE_SIZE
|
||||
data = self.get_raid_instance_chars()
|
||||
return self.format_pagination(data, offset, page, self.compact_char_display, 'Raid Instances',
|
||||
'No Raidinstances found.', 'riadmin ', self.PAGE_SIZE, blob)
|
||||
|
||||
def format_pagination(self, data, offset, page, formatter, title, nullmsg, cmd, page_size=10, headline=""):
|
||||
raid_instances = self.get_raid_instances()
|
||||
blob = ""
|
||||
if page == 1:
|
||||
blob = headline
|
||||
selected = data[offset:offset + page_size]
|
||||
if len(data) == 0:
|
||||
return nullmsg
|
||||
num_assigned = 0
|
||||
num_unassigned = 0
|
||||
pages = ""
|
||||
|
||||
if page > 1:
|
||||
pages += "Pages: " + self.text.make_tellcmd("«« Page %d" % (page - 1), f'{cmd} --page={page - 1}')
|
||||
if offset + page_size < len(data):
|
||||
pages += f" Page {page}/{math.ceil(len(data) / page_size)}"
|
||||
pages += " " + self.text.make_tellcmd("Page %d »»" % (page + 1), f'{cmd} --page={page + 1}')
|
||||
pages += "\n"
|
||||
current_raid_instance_id = None
|
||||
for row in data:
|
||||
if row.raid_instance_id == self.UNASSIGNED_RAID_INSTANCE_ID:
|
||||
num_unassigned += 1
|
||||
else:
|
||||
if row.name:
|
||||
num_assigned += 1
|
||||
blob += "" + pages
|
||||
for row in selected:
|
||||
if row.raid_instance_id != current_raid_instance_id:
|
||||
name = ""
|
||||
if row.bot:
|
||||
bot = self.pork.get_character_info(row.bot)
|
||||
name = f"[{bot.name}]" if bot else ""
|
||||
blob += f"\n<header2>{row.raid_instance_name} {name}</header2>\n"
|
||||
current_raid_instance_id = row.raid_instance_id
|
||||
|
||||
if not row.char_id:
|
||||
continue
|
||||
|
||||
blob += formatter(row)
|
||||
add_leader_link = (row.raid_instance_id != self.UNASSIGNED_RAID_INSTANCE_ID and not row.is_leader)
|
||||
blob += " " + self.get_assignment_links(raid_instances, row.name, add_leader_link)
|
||||
blob += "\n"
|
||||
blob += "\n" + pages
|
||||
out = ChatBlob(title, blob)
|
||||
out.page_postfix = f" (<highlight>{num_assigned}</highlight> Players)"
|
||||
if num_unassigned > 0:
|
||||
out.page_postfix = f" (Assigned: <highlight>{num_assigned}</highlight>, " \
|
||||
f"Unassigned: <highlight>{num_unassigned}</highlight>)"
|
||||
return out
|
||||
|
||||
@command(command="ritake", params=[Any("raiders")],
|
||||
access_level="all",
|
||||
description="take the RI from another bot")
|
||||
def ritake(self, request, raiders: str):
|
||||
users = raiders.split(",")
|
||||
if request.sender.char_id not in eval(self.setting_service.get_value('riadmin_network')):
|
||||
return
|
||||
for user in users:
|
||||
char = self.character_service.resolve_char_to_id(user.strip())
|
||||
if not char:
|
||||
continue
|
||||
if self.private_channel_service.in_private_channel(char):
|
||||
self.buddy_service.add_buddy(char, 'raider')
|
||||
continue
|
||||
self.bot.send_mass_message(char, 'You have been assigned to my RI :: <red>accept the invite!</red>')
|
||||
self.private_channel_service.invite(char)
|
||||
|
||||
@command(command="riadmin",
|
||||
params=[Const("add"), Any("raid_instance"), Character("char")],
|
||||
access_level="leader",
|
||||
description="Add a character to a RI", sub_command="leader")
|
||||
def riadmin_add(self, _, _1, raid_instance_name, char):
|
||||
if not char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
raid_instance = self.get_raid_instance(raid_instance_name)
|
||||
if not raid_instance:
|
||||
return f"Raid instance <highlight>{raid_instance_name}</highlight> does not exist."
|
||||
|
||||
self.refresh_raid_instance_chars()
|
||||
|
||||
row = self.db.query_single("SELECT raid_instance_id FROM raid_instance_char WHERE char_id = ?", [char.char_id])
|
||||
if row:
|
||||
if raid_instance.id == row.raid_instance_id:
|
||||
if 'raider' not in (
|
||||
self.buddy_service.get_buddy(char.char_id) or DictObject({'types': []}).get('types', [])):
|
||||
self.buddy_service.add_buddy(char.char_id, 'raider')
|
||||
return f"Character <highlight>{char.name}</highlight> is already assigned to " \
|
||||
f"raid instance <highlight>{raid_instance.name}</highlight>."
|
||||
else:
|
||||
if 'raider' not in (
|
||||
self.buddy_service.get_buddy(char.char_id) or DictObject({'types': []}).get('types', [])):
|
||||
self.buddy_service.add_buddy(char.char_id, 'raider')
|
||||
self.update_char_raid_instance(char.char_id, raid_instance.id)
|
||||
return f"Character <highlight>{char.name}</highlight> has been assigned to " \
|
||||
f"raid instance <highlight>{raid_instance.name}</highlight>."
|
||||
else:
|
||||
return f"Character <highlight>{char.name}</highlight> is not in the private channel."
|
||||
|
||||
@command(command="riadmin", params=[Const("clear")], access_level="leader",
|
||||
description="Remove all raids and their players", sub_command="leader")
|
||||
def riadmin_clear(self, _, _1):
|
||||
query = self.db.query("SELECT * FROM raid_instance_char")
|
||||
for user in query:
|
||||
self.buddy_service.remove_buddy(user.char_id, 'raider')
|
||||
self.db.exec("DELETE FROM raid_instance_char where 1")
|
||||
self.db.exec("DELETE FROM raid_instance where 1")
|
||||
return f"All characters have been removed from raid instances."
|
||||
|
||||
@command(command="riadmin", params=[Const("rem"), Character("char")], access_level="leader",
|
||||
description="remove a character from the RI", sub_command="leader")
|
||||
def riadmin_rem(self, _, _2, char):
|
||||
if not char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
|
||||
self.refresh_raid_instance_chars()
|
||||
|
||||
row = self.db.query_single(
|
||||
"SELECT r2.name FROM raid_instance_char r1 "
|
||||
"JOIN raid_instance r2 ON r1.raid_instance_id = r2.id WHERE r1.char_id = ?",
|
||||
[char.char_id])
|
||||
if row:
|
||||
self.update_char_raid_instance(char.char_id, self.UNASSIGNED_RAID_INSTANCE_ID)
|
||||
return f"Character <highlight>{char.name}</highlight> has been removed from " \
|
||||
f"raid instance <highlight>{row.name}</highlight>."
|
||||
else:
|
||||
return f"Character <highlight>{char.name}</highlight> is not assigned to any raid instances."
|
||||
|
||||
@command(command="riadmin", params=[Const("leader"), Character("char")], access_level="leader",
|
||||
description="Set the leader for a RI", sub_command="leader")
|
||||
def riadmin_leader(self, _, _2, char):
|
||||
if not char.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": char.name})
|
||||
|
||||
raid_instance = self.get_raid_instance_by_char(char.char_id)
|
||||
if not raid_instance:
|
||||
return f"Character <highlight>{char.name}</highlight> does not belong to a raid instance."
|
||||
|
||||
self.set_leader(char.char_id, raid_instance.id)
|
||||
|
||||
return f"Character <highlight>{char.name}</highlight> has been set as the leader for " \
|
||||
f"raid instance <highlight>{raid_instance.name}</highlight>."
|
||||
|
||||
@command(command="riadmin", params=[Const("send")], access_level="leader",
|
||||
description="Send the RIs to their preset bots", sub_command="leader")
|
||||
def riadmin_send(self, _, _2):
|
||||
self.bot.send_private_channel_message("Exporting raids..")
|
||||
for raid_instance in self.get_raid_instances():
|
||||
data = self.db.query(
|
||||
"SELECT r.char_id, p.name, r.is_leader FROM raid_instance_char r "
|
||||
"left join player p on r.char_id=p.char_id WHERE raid_instance_id = ?",
|
||||
[raid_instance.id])
|
||||
if int(raid_instance.bot) == self.bot.get_char_id() or raid_instance.bot == "0" or not raid_instance.bot:
|
||||
continue
|
||||
self.bot.send_private_message(int(raid_instance.bot), "ritake " + ", ".join([k['name'] for k in data]),
|
||||
add_color=False)
|
||||
for char in data:
|
||||
if char.is_leader == 0:
|
||||
self.private_channel_service.kick(char.char_id)
|
||||
|
||||
return "Raid instance configuration has been applied."
|
||||
|
||||
@command(command="riadmin", params=[Const("create"), Any("Raid_name"), Character("bot")],
|
||||
access_level="leader",
|
||||
description="Create or change a raid instance", sub_command="leader")
|
||||
def riadmin_create(self, _, _2, raid_instance_name, bot):
|
||||
if not bot.char_id:
|
||||
return self.getresp("global", "char_not_found", {"char": bot.name})
|
||||
if bot.char_id not in eval(self.setting_service.get_value('riadmin_network')):
|
||||
return "Bot not valid: please ask an Administrator to verify it first."
|
||||
raid_instance = self.get_raid_instance(raid_instance_name)
|
||||
if raid_instance:
|
||||
if raid_instance.name == raid_instance_name and raid_instance.bot == bot.char_id:
|
||||
return f"Raid instance <highlight>{raid_instance_name}</highlight> already exists."
|
||||
else:
|
||||
self.db.exec("UPDATE raid_instance SET name = ?, bot = ? WHERE id = ?",
|
||||
[raid_instance_name, bot.char_id, raid_instance.id])
|
||||
return f"Raid instance <highlight>{raid_instance_name}</highlight> has been updated."
|
||||
else:
|
||||
self.db.exec("INSERT INTO raid_instance (name, bot) VALUES (?, ?)", [raid_instance_name, bot.char_id])
|
||||
return f"Raid instance <highlight>{raid_instance_name}</highlight> [<highlight>{bot.name}</highlight>] " \
|
||||
f"has been created."
|
||||
|
||||
@command(command="riadmin", params=[Const("delete"), Any("raid_instance_name")], access_level="leader",
|
||||
description="Remove a RI", sub_command="leader")
|
||||
def raid_instance_delete_cmd(self, _, _1, raid_instance_name):
|
||||
raid_instance = self.get_raid_instance(raid_instance_name)
|
||||
if not raid_instance:
|
||||
return f"Raid instance <highlight>{raid_instance_name}</highlight> does not exist."
|
||||
query = self.db.query("SELECT char_id from raid_instance_char where raid_instance_id=?", [raid_instance.id])
|
||||
for user in query:
|
||||
self.buddy_service.remove_buddy(user.char_id, 'raider')
|
||||
self.db.exec("DELETE FROM raid_instance_char WHERE raid_instance_id = ?", [raid_instance.id])
|
||||
self.db.exec("DELETE FROM raid_instance WHERE id = ?", [raid_instance.id])
|
||||
|
||||
return f"Raid instance <highlight>{raid_instance_name}</highlight> has been deleted."
|
||||
|
||||
def get_raid_instance_chars(self):
|
||||
self.refresh_raid_instance_chars()
|
||||
|
||||
data = self.db.query("SELECT * FROM ("
|
||||
"SELECT p.*, r2.id AS raid_instance_id, "
|
||||
"r1.is_leader, r2.name AS raid_instance_name, r2.bot "
|
||||
"FROM raid_instance r2 "
|
||||
"LEFT JOIN raid_instance_char r1 ON r1.raid_instance_id = r2.id "
|
||||
"LEFT JOIN player p ON r1.char_id = p.char_id "
|
||||
"UNION "
|
||||
"SELECT p.*, r3.raid_instance_id, "
|
||||
"r3.is_leader, 'Unassigned' AS raid_instance_name, '' AS bot "
|
||||
"FROM raid_instance_char r3 "
|
||||
"LEFT JOIN player p ON r3.char_id = p.char_id "
|
||||
"WHERE r3.raid_instance_id = ?) as r2r1pr3p "
|
||||
"ORDER BY raid_instance_id != ? DESC, "
|
||||
"raid_instance_name, "
|
||||
"is_leader desc, "
|
||||
"profession, level desc, "
|
||||
"name",
|
||||
[self.UNASSIGNED_RAID_INSTANCE_ID, self.UNASSIGNED_RAID_INSTANCE_ID])
|
||||
|
||||
return data
|
||||
|
||||
def refresh_raid_instance_chars(self):
|
||||
users = self.db.query('SELECT * from online o '
|
||||
'left join player p on o.char_id=p.char_id '
|
||||
'where o.channel = ?', [self.bot.name])
|
||||
for user in users:
|
||||
self.db.exec(
|
||||
"INSERT IGNORE INTO raid_instance_char (char_id, raid_instance_id, is_leader) VALUES (?, ?, 0)",
|
||||
[user.char_id, self.UNASSIGNED_RAID_INSTANCE_ID])
|
||||
self.buddy_service.add_buddy(user.char_id, 'raider')
|
||||
|
||||
def update_char_raid_instance(self, char_id, raid_instance_id):
|
||||
return self.db.exec("UPDATE raid_instance_char SET raid_instance_id = ?, is_leader = 0 WHERE char_id = ?",
|
||||
[raid_instance_id, char_id])
|
||||
|
||||
def set_leader(self, char_id, raid_instance_id):
|
||||
self.db.exec("UPDATE raid_instance_char SET is_leader = 0 WHERE raid_instance_id = ?", [raid_instance_id])
|
||||
self.db.exec("UPDATE raid_instance_char SET is_leader = 1 WHERE raid_instance_id = ? AND char_id = ?",
|
||||
[raid_instance_id, char_id])
|
||||
|
||||
def compact_char_display(self, char_info):
|
||||
if char_info.level:
|
||||
msg = f" {self.util.get_prof_icon(char_info.profession)} " \
|
||||
f"{self.text.zfill(char_info.level, 220)}/<green>{self.text.zfill(char_info.ai_level, 30)}</green> " \
|
||||
f"{char_info.name: <13}"
|
||||
elif char_info.name:
|
||||
msg = " <highlight>%s</highlight>" % char_info.name
|
||||
else:
|
||||
msg = " <highlight>Unknown(%d)</highlight>" % char_info.char_id
|
||||
|
||||
if char_info.is_leader:
|
||||
msg += " [<highlight>Leader</highlight>]"
|
||||
|
||||
return msg
|
||||
|
||||
def get_assignment_links(self, raid_instances, char_name, add_leader_link):
|
||||
links = list(map(lambda x: self.text.make_tellcmd(x.name, f"riadmin add {x.name} {char_name}"), raid_instances))
|
||||
links.insert(0, self.text.make_tellcmd("Rem", f"riadmin rem {char_name}"))
|
||||
if add_leader_link:
|
||||
links.insert(0, self.text.make_tellcmd("L", f"riadmin leader {char_name}"))
|
||||
return f'[{"|".join(links)}]'
|
||||
|
||||
def get_raid_instances(self):
|
||||
data = self.db.query("SELECT id, name, bot FROM raid_instance ORDER BY name")
|
||||
return data
|
||||
|
||||
def get_raid_instance(self, raid_instance_name):
|
||||
return self.db.query_single("SELECT id, name, bot FROM raid_instance WHERE name LIKE ?", [raid_instance_name])
|
||||
|
||||
def get_raid_instance_by_char(self, char_id):
|
||||
return self.db.query_single("SELECT id, name, CASE WHEN bot IS NOT NULL THEN bot else 0 END as bot "
|
||||
"FROM raid_instance r1 JOIN raid_instance_char r2 ON r1.id = r2.raid_instance_id "
|
||||
"WHERE r2.char_id = ?", [char_id])
|
||||
|
||||
def get_conn_by_id(self, bot):
|
||||
conn = self.bot.conns.get(bot)
|
||||
if conn:
|
||||
return conn
|
||||
|
||||
conns = self.bot.get_conns(lambda x: x.char_name.lower() == bot.lower())
|
||||
if conns:
|
||||
return conns[0][1]
|
||||
|
||||
return None
|
||||
@@ -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})
|
||||
@@ -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})
|
||||
@@ -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})
|
||||
@@ -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)})
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
@@ -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") + ".."
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user