Initial Release of IGNCore version 2.5

This commit is contained in:
2021-08-09 13:18:56 +02:00
commit a83d98c47e
910 changed files with 224171 additions and 0 deletions
@@ -0,0 +1,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
+66
View File
@@ -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."
}
}
+188
View File
@@ -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