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,68 @@
from threading import Thread
from core import command_request, sender_obj
from core.aochat.BaseModule import BaseModule
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, 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.text import Text
from core.tyrbot import Tyrbot
from core.util import Util
# noinspection DuplicatedCode
@instance()
class BotController(BaseModule):
bots = []
threads = {}
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.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")
def pre_start(self):
self.db.exec("CREATE TABLE IF NOT EXISTS org_bots(char_id int primary key not null, org_id int not null)")
def start(self):
pass
@command(command="bots", params=[Const("add"), Character("botname")], access_level="admin",
description="Add an bot to the bot list")
def bots_add_any(self, sender, _, bot):
Thread(target=self.bots_add, args=(bot, sender)).start()
def bots_add(self, bot: sender_obj, request: command_request):
player = self.pork.request_char_info(bot.name, 5)
if self.db.exec("INSERT IGNORE INTO org_bots(char_id, org_id) VALUES(?, ?)", [bot.char_id, player.org_id]) == 0:
request.reply("The bot <highlight>%s<end> is already marked as an chatbot." % bot.name)
else:
request.reply("Successfully marked <highlight>%s<end> as an chatbot." % bot.name)
@command(command="bots",
params=[],
access_level="moderator",
description="show all orgbots",
sub_command="show")
def bots_show_all(self, _):
def format_row(query):
bud = self.buddy_service.is_online(query["char_id"])
buddy = "<green>O<end>" if bud == 1 else "<red>O<end>" if bud == 0 else "<grey>U<end>"
return "- [{status}] <highlight>{name}<end> (<notice>{org_name}<end>)\n".format(**query, status=buddy)
data = self.db.query("SELECT * FROM org_bots o LEFT JOIN player p on o.char_id = p.char_id order by p.org_name")
blob = ""
for row in data:
blob += format_row(row)
return ChatBlob("Our Orgbots", blob)
@@ -0,0 +1,35 @@
from core.command_param_types import Const, Int, Any
from core.decorators import instance, command
from modules.standard.online.online_controller import OnlineController
@instance(name="online_controller", override=True)
class OrgOnlineController(OnlineController):
@command(command="online", params=[Const('all', is_optional=True),
Int("min_level", is_optional=True),
Any("profession", is_optional=True)],
description="shows online players",
access_level="member")
def online_all_cmd(self, request, const_all, min_level, profession):
query = ""
params = [self.bot.name, self.bot.get_char_id()]
priv = self.priv.in_private_channel(request.sender.char_id)
if priv:
if const_all:
query += "and channel_id IN (1, 2, 3) "
else:
query += "and channel_id IN (1, 2) "
else:
query += "and channel_id = 3 "
if min_level:
query += "and p.level >= ? "
params.append(min_level)
if profession:
query += "and p.profession = ? "
params.append(self.util.get_profession(profession))
if priv and not const_all:
blob = self.online_display.format_by_channel_prof(query, params)
else:
blob = self.online_display.format_by_org(query, params)
self.bot.send_mass_message(request.sender.char_id, self.online_display.format_blob(blob))
@@ -0,0 +1,38 @@
from core.buddy_service import BuddyService
from core.command_alias_service import CommandAliasService
from core.db import DB
from core.decorators import instance
from core.logger import Logger
from core.lookup.pork_service import PorkService
from core.text import Text
from core.tyrbot import Tyrbot
from core.util import Util
from modules.core.accounting.services.access_service import AccessService
# noinspection DuplicatedCode
@instance()
class OrgAliasController:
org_prefix = {4736: "AP", 9632: "AP", 9622: "AC", 9831: "AC", 581633: "BST", 9707: "CoH", 9990: "CoH", 4637: "HAV",
10197: "IMP", 6093: "LCG", 4789: "MJ", 4687: "NA", 4800: "PR", 4993: "REGS", 813067: "SOTL",
4851: "SP", 9611: "SP", 4614: "TA", 4831: "TGNF", 6183: "TRE", 9822: "TS", 4611: "UHS",
5571: "UOTR", 520210: "WO", 1349649: "VR", 4826: "42", 1349647: "VR", 6332: "DRA", 848: "DRA",
4675: "VA", 1558530: "EoS"}
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.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.access_service: AccessService = registry.get_instance("access_service")
def start(self):
self.db.exec("CREATE TABLE IF NOT EXISTS org_alias(org_id int primary key not null, org_alias VARCHAR(255))")
def get_alias(self, org_id):
return self.org_prefix.get(org_id, "-UKN-")
+369
View File
@@ -0,0 +1,369 @@
import json
import re
import time
from threading import Thread
import requests
from mysql.connector.cursor import CursorBase
from core.buddy_service import BuddyService
from core.cache_service import CacheService
from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService
from core.command_param_types import Const, Any
from core.command_request import CommandRequest
from core.db import DB
from core.decorators import instance, command, timerevent, event
from core.dict_object import DictObject
from core.logger import Logger
from core.lookup.org_pork_service import OrgPorkService
from core.lookup.pork_service import PorkService
from core.message_hub_service import MessageHubService
from core.text import Text
from core.tyrbot import Tyrbot
from core.util import Util
from modules.core.accounting.services.access_service import AccessService
# noinspection PyAttributeOutsideInit,SpellCheckingInspection,DuplicatedCode
from modules.core.accounting.services.account_service import AccountService
from modules.onlinebot.online.org_alias_controller import OrgAliasController
# noinspection DuplicatedCode
@instance()
class OrgController:
threads = {}
letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
"v", "w", "x", "y", "z", "others", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
pork_uri = "https://people.anarchy-online.com/people/lookup/orgs.html?l=%s"
single_org_uri = "https://people.anarchy-online.com/org/stats/d/5/name/%d/basicstats.xml?data_type=json"
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.pork: PorkService = registry.get_instance("pork_service")
self.org_pork: OrgPorkService = 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.access_service: AccessService = registry.get_instance("access_service")
self.relay_hub_service: MessageHubService = registry.get_instance("message_hub_service")
self.alias_controller: OrgAliasController = registry.get_instance("org_alias_controller")
self.account_service: AccountService = registry.get_instance("account_service")
self.cache: CacheService = registry.get_instance("cache_service")
def start(self):
self.db.exec("CREATE TABLE IF NOT EXISTS orgs(org_id int primary key not null)")
self.command_alias_service.add_alias("org", "orgs")
@event("connect", "Adds all members to buddylist")
def connect(self, _, _1):
query = self.db.query("SELECT char_id, member from account where member IN (SELECT org_id from orgs)")
if query:
for player in query:
self.buddy_service.add_buddy(player.char_id, "member")
@command(command="orgs", params=[Const("add"), Any("Organisation")], access_level="admin",
description="Add an org to the online list")
def orgs_add_any(self, sender, _, org):
return self.orgs_add(org, sender)
def orgs_add(self, search, sender):
orgs = self.find_org(search)
if len(orgs) == 1:
orgs = orgs[0]
if self.db.exec("REPLACE INTO orgs(org_id) VALUES(?)",
[orgs.org_id]) == 1:
sender.reply("Adding the organisation <highlight>%s<end> to the roster..." % orgs.org_name)
org_adder = Thread(name=orgs.org_id, target=self.fetch_single,
args=(orgs.org_id, orgs.org_name, sender))
self.threads[orgs.org_id] = org_adder
org_adder.start()
else:
return "The organisation <highlight>%s<end> is in the roster already." % orgs.org_name
elif len(orgs) == 0:
return "No org with the name <highlight>%s<end> was found on PoRK." % search
else:
blob = "Your search had multiple results; please pick an org:<br>"
for org in orgs:
blob += "[%s][%s] <highlight>%s<end> (<highlight>%s<end>) <%s>%s<end> [<highlight>%s<end> " \
"members]<br><pagebreak>" \
% (self.text.make_chatcmd("Add", "/tell <myname> orgs add %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="orgs", params=[Const("rem"), Any("Organisation")], access_level="admin",
description="Remove an org from the online list")
def orgs_rem_any(self, _, _1, org):
return self.orgs_rem(org)
def orgs_rem(self, search):
orgs = self.find_org(search)
if len(orgs) == 1:
orgs = orgs[0]
if self.db.exec("DELETE FROM orgs where org_id = ?", [orgs.org_id]) == 1:
org_remover = Thread(name=orgs.org_id, target=self.remove_single, args=(orgs.org_id, orgs.org_name))
self.threads[orgs.org_id] = org_remover
org_remover.start()
return "Removed the organisation <highlight>%s<end> from the roster." % orgs.org_name
else:
return "The organisation <highlight>%s<end> is not on the roster list." % orgs.org_name
elif len(orgs) == 0:
return "The organisation <highlight>%s<end> is not on the roster list." % search
else:
blob = "Your search had multiple results; please pick an org:<br>"
for org in orgs:
blob += "[%s][%s] <highlight>%s<end> (<highlight>%s<end>) <%s>%s<end> [<highlight>%s<end> " \
"members]<br><pagebreak>" \
% (self.text.make_chatcmd("Remove", "/tell <myname> orgs 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="orgs", params=[Const("list", is_optional=True)], access_level="member",
description="View all orgs on the online list", sub_command="list")
def orgs_list(self, sender: CommandRequest, _):
head = "<header>Organisations in our Alliance<end>"
blob = ""
for org in self.db.query("SELECT * from orgs o "
"left join all_orgs a on o.org_id = a.org_id order by a.org_name"):
org = DictObject(org)
blob += "- %s%s<highlight>%s<end> (%d) with <highlight>%d<end> members\n" % (
"[<highlight>" + self.text.make_chatcmd("Info", "/tell <myname> orgs info %d" % org.org_id,
style="style='text-decoration:none'") + "</highlight>] ",
"[<red>" + self.text.make_chatcmd("Remove", "/tell <myname> orgs rem %d" % org.org_id,
style="style='text-decoration:none'") + "</red>] " if
sender.sender.access_level["level"] <= 10 else "",
org.org_name, org.org_id,
self.db.query_single("SELECT member_count from all_orgs where org_id=?", [org.org_id]).member_count)
return ChatBlob(head, blob)
@timerevent(budatime="48h", description="pull list of all orgs from PoRK")
def discover_orgs(self, _, _1):
def discover():
start = time.time()
self.logger.info("Fetching global orgdata..")
count = 0
data = []
for letter in self.letters:
result = requests.get(self.pork_uri % letter)
# noinspection RegExpRepeatedSpace
matches = re.findall("""<tr>
<td align="left">
<a href="//people.anarchy-online.com/org/stats/d/5/name/(\d+)">
(.+)</a></td>
<td align="right">(\d+)</td>
<td align="right">(\d+)</td>
<td align="left">(\w+)</td>
<td align="left">(\w+)</td>
<td align="left" class="dim">RK5</td>
</tr>""", result.text)
for match in matches:
if int(match[2]) < 6:
continue
data.append((int(match[0]), match[1], int(match[2]), match[4], start))
count += 1
self.logger.info("Batch %s done!" % letter)
with self.db.pool.get_connection() as conn:
with conn.cursor() as cur:
cur: CursorBase
cur.executemany(
"INSERT INTO all_orgs(org_id, org_name, member_count, faction, last_seen) "
"VALUES(?, ?, ?, ?, ?) "
"ON DUPLICATE KEY UPDATE "
"org_name=VALUE(org_name), "
"member_count=VALUE(member_count), "
"last_seen=VALUE(last_seen)",
data)
self.db.exec("DELETE FROM all_orgs where last_seen < ?", [time.time() - 2 * 24 * 60 * 60])
self.logger.info("Successfully fetched %d orgs in %d seconds." % (count, time.time() - start))
self.threads.pop('orgdiscover', None)
if "orgdiscover" not in self.threads.keys():
thread = Thread(name="orgdiscover", target=discover, daemon=True)
self.threads["orgdiscover"] = thread
thread.start()
@timerevent(budatime="24h", description="Pull data for our own orgs")
def fetch_orgs(self, _, _1):
def discover():
start = time.time()
self.logger.info("Fetching orgdata..")
output = []
data = []
accounts = []
ours = self.db.query("SELECT o.org_id, a.org_name from orgs o "
"left join all_orgs a on o.org_id = a.org_id order by lower(a.org_name)")
for org in ours:
result = requests.get(self.single_org_uri % org.org_id).json()
if result:
self.cache.store('org_roster', f"{org.org_id}.5.json", json.dumps(result))
else:
result = json.loads(self.cache.retrieve('org_roster', f"{org.org_id}.5.json").data)
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"],
result[0]["ORG_INSTANCE"],
start, start))
if not self.buddy_service.get_buddy(char_info["CHAR_INSTANCE"]):
self.buddy_service.add_buddy(char_info["CHAR_INSTANCE"], "member")
output.append(DictObject({"action": "JOIN",
"name": char_info['NAME'],
"org_name": result[0]["NAME"],
"org_id": result[0]["ORG_INSTANCE"],
"level": char_info["LEVELX"],
"ai_level": char_info["ALIENLEVEL"],
"ranks": 0}))
self.logger.info("Organisation %s has been updated." % org.org_name)
self.account_service.create_users(accounts)
if len(data) > 1:
with self.db.lock:
with self.db.pool.get_connection() as conn:
with conn.cursor() as cur:
cur: CursorBase
cur.executemany("INSERT 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 "
"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
"ON DUPLICATE KEY UPDATE first_name=VALUE(first_name), "
"last_name=VALUE(last_name), level=VALUE(level), "
"breed=VALUE(breed), gender=VALUE(gender), "
"faction=VALUE(faction), profession=VALUE(profession), "
"profession_title=VALUE(profession_title),ai_rank=VALUE(ai_rank), "
"ai_level=VALUE(ai_level), org_name=VALUE(org_name), "
"org_id=VALUE(org_id), org_rank_name=VALUE(org_rank_name), "
"org_rank_id=VALUE(org_rank_id), source=VALUE(source), "
"last_updated=VALUE(last_updated)", data)
conn.commit()
self.db.exec("UPDATE account set member = -1 "
"where char_id NOT IN "
"(select char_id from player where org_id in (select org_id from orgs)) and member > 0")
self.db.exec("DELETE FROM ranks where main not in (select main from account where member > -1)")
players = self.db.query("SELECT a.char_id, p.* FROM account a "
"left join player p on a.char_id=p.char_id "
"where (a.last_updated < ? and member >1) or "
"(p.org_id NOT IN (select org_id from orgs) and a.member>1) ",
[time.time() - 24 * 60 * 60])
accounts = []
for player in players:
bonus = None
player = DictObject(player)
count = 0
if self.buddy_service.remove_buddy(player.char_id, "member"):
count = self.db.exec("DELETE FROM ranks where main=?", [player.char_id])
accounts.append((player.char_id, 1))
bonus = "LEAVE"
new_data = self.pork.request_char_info(player.name, player.dimension)
if new_data and new_data.char_id == player.char_id:
self.pork.save_character_info(new_data)
else:
bonus = "DEL"
accounts.append((player.char_id, 1))
if bonus:
output.append(DictObject({"action": bonus,
"name": player.name,
"org_name": player.org_name,
"org_id": player.org_id,
"level": player.level,
"ai_level": player.ai_level,
"ranks": count}))
self.account_service.remove_members(accounts)
self.log(output, time.time() - start)
self.logger.info("Successfully fetched %d players from %d orgs in %d seconds. - " % (
len(data), len(ours), time.time() - start))
del self.threads['roster']
if "roster" not in self.threads.keys():
thread = Thread(name="roster", target=discover, daemon=True)
self.threads["roster"] = thread
thread.start()
def find_org(self, search, table="all_orgs"):
if search.isdigit():
return self.db.query("SELECT * FROM " + table + " where org_id = ?", [search])
elif isinstance(search, str):
return self.db.query("SELECT * FROM " + table + " where org_name LIKE ?", ["%" + search + "%"])
def fetch_single(self, org_id, org_name, sender: object):
start = time.time()
data = []
accounts = []
self.logger.info("Fetching orgdata..")
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"], result[0]["ORG_INSTANCE"],
start, start))
self.buddy_service.add_buddy(char_info['CHAR_INSTANCE'], "member")
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)
self.logger.info("Organisation %s added!" % org_name)
sender.reply(f"<highlight>{org_name}</highlight> has been added to the roster. "
f"Runtime: {time.time() - start:.2f} seconds.")
self.logger.info("Successfully fetched %d players in %d seconds." % (count, time.time() - start))
del self.threads[org_id]
def remove_single(self, org_id, org_name):
members = self.db.query("SELECT * from player where org_id=?", [org_id])
for member in members:
self.buddy_service.remove_buddy(member.char_id, "member")
self.db.exec("UPDATE account set member=-1 where char_id in"
" (SELECT char_id from player where org_id=?)", [org_id])
self.db.exec("DELETE FROM online where char_id in (SELECT char_id from player where org_id=?)", [org_id])
self.logger.info("Organisation %s removed!" % org_name)
del self.threads[org_id]
def log(self, blob, duration):
out = []
s = []
current = ""
for entry in blob:
s.append(f"[{self.alias_controller.get_alias(entry.org_id)}] [{entry.action}] {entry.name} "
f"({entry.level}/{entry.ai_level}) {'[R-P]' if entry.ranks > 0 else ''}\n")
s = sorted(s)
if len(s) > 0:
s.append(f"\nRuntime: {duration:.2f} seconds.")
for entry in s:
if len(current) > 1500:
out.append(current)
current = ""
current += entry
if len(current) > 10:
out.append(current)
if len(out) > 0:
self.relay_hub_service.send_message("member_logger", None, out, out)