[-] plugins (?)

[+] Optimize project
[+] config_dir (?)
This commit is contained in:
Maxim Khomutov 2024-01-24 17:32:45 +03:00
parent 2710cb6e14
commit c12cdb1630
14 changed files with 326 additions and 367 deletions

View File

@ -5,3 +5,4 @@ ruamel.yaml~=0.18.5
requests~=2.31.0
loguru~=0.7.2
easydict~=1.11
SQLAlchemy~=2.0.25

View File

@ -1 +1,256 @@
import glob
import os
import platform
import signal
import sys
import zipfile
from datetime import datetime
import requests
from loguru import logger
from ruamel.yaml import YAML
yaml = YAML()
yaml.default_flow_style = False
IN_DOCKER = "IN_DOCKER" in os.environ
__version__ = '2.1.0'
config_dir = "config/" if IN_DOCKER else ""
raw_config_main = f"""\
# Работа протестирована на LongPool 5.199
vk_token: "" # Токен ВК
help_file: {config_dir}help_message.txt # Файл, который будет выводиться на !help
perms_file: {config_dir}permissions.yml # Конфиг с настройками разрешений для пользователей
hosts_file: {config_dir}hosts.yml # Конфиг с настройками хостов
store_file: {config_dir}store/sql.yml # Конфиг с настройками где хранить историю онлайна
"""
raw_config_perms = """\
noRole: Нет роли
noRights: Нет прав # null для отключения
noNick: Не указан # Используется для !id
# Таблица соответствия vkID к нику в Майнкрафте
# Ник будет передаваться в плагины (Плагины бота)
nicks:
370926160: Rick
583018016: SantaSpeen
perms:
admin: # Имя группы
name: Админ # Имя группы, которое будет отображаться в боте
ids: # вк ИД входящих в состав группы
- 370926160
parent: # Наследование прав
- helper
allow: # Права, подробнее в readme.md
- bot.*
# - bot.help
# - bot.info
# - bot.hosts
# - bot.hosts.*
# - bot.hosts.list
# - bot.hosts.reload
# - bot.perms
# - bot.hosts.*
# - bot.hosts.reload
# - bot.cmd.*
# - bot.cmd.help
# - bot.cmd.id
# - bot.online.*
# - bot.online.default
# - bot.online.lobby
# - bot.history.*
# - bot.history.default
# - bot.history.lobby
# - bot.rcon.*
# - bot.rcon.*.*
helper:
name: Хелпер
ids:
- 583018016
allow:
- bot.rcon.default
- bot.rcon.lobby
- bot.rcon.survival
- bot.rcon.*.say
- bot.rcon.*.mute
- bot.rcon.survival.ban
- bot.rcon.survival.tempban
default:
name: Игрок
allow:
- bot.cmd.help
- bot.cmd.id
- bot.cmd.online.*
- bot.cmd.history.*
"""
raw_config_hosts = """\
hosts:
survival: # Название сервера (имя), может быть любым, может быть сколько угодно
meta:
name: Выживание # Это имя будет выводиться в ответе, или статистике (имя для бота)
java: true # Это JAVA сервер?
important: true # Если да, то бот не включится, если не подключится
# 0 - выключен
# 1 - Доступно без имени (!! Такое значение может быть только у 1 хоста !!) (.rcon <cmd>)
# 2 - Доступно с именем (.rcon <name> <cmd>)
# Разрешение: bot.rcon.<name>; bot.online.<name>; bot.history.<name>
# При запуске бота будет проверка доступности всего
rcon: 2 # RCON будет доступен по команде .rcon survival <cmd> (разрешение: bot.rcon.survival)
# !online будет доступен по команде !online survival (разрешение: bot.cmd.online.survival)
# !history будет доступен по команде !history survival (разрешение: bot.cmd.history.survival)
online: 2
rcon: # RCON подключение
host: 192.168.0.31
port: 15101
password: rconPass1
mine: # Minecraft подключение (нужно для !online и !history)
host: 192.168.0.31
port: 15001
lobby:
meta:
name: Лобби
important: true
java: true
rcon: 1
online: 2
rcon:
host: 192.168.0.31
port: 15100
password: rconPass2
mine:
host: 192.168.0.31
port: 15000
devlobby:
meta:
name: Лобби
important: false
java: true
rcon: 2
online: 2
rcon:
host: 192.168.0.31
port: 15108
password: rconPass3
mine:
host: 192.168.0.31
port: 15008
proxy-local:
meta:
name: Proxy-Local
java: true
important: true
rcon: 0
online: 1
rcon:
mine:
host: 192.168.0.31
port: 15009
"""
raw_help = """\
!help - Вывести это сообщение
!online - Показать текущий онлайн на сервере
"""
config_file_main = config_dir + "bot.yml"
if config_dir and not os.path.exists(config_dir):
os.makedirs(config_dir)
def init_logger():
log_debug = "./logs/debug.log"
log_file = "./logs/latest.log"
log_dir = os.path.dirname(log_file) + "/"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
if os.path.exists(log_file):
ftime = os.path.getmtime(log_file)
zip_path = log_dir + datetime.fromtimestamp(ftime).strftime('%Y-%m-%d') + "-%s.zip"
index = 1
while True:
if not os.path.exists(zip_path % index):
break
index += 1
with zipfile.ZipFile(zip_path % index, "w") as zipf:
logs_files = glob.glob(f"{log_dir}/*.log")
for file in logs_files:
if os.path.exists(file):
zipf.write(file, os.path.basename(file))
os.remove(file)
logger.remove(0)
logger.add(log_debug, level=0, backtrace=True, diagnose=True)
logger.add(log_file, level="INFO", backtrace=False, diagnose=False,
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}")
logger.add(sys.stdout, level="INFO", backtrace=False, diagnose=False,
format="\r<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | {message}")
init_logger()
if not os.path.exists(config_file_main):
logger.info(f"Создание: {config_file_main}...")
c = yaml.load(raw_config_main)
with open(config_file_main, "w", encoding="utf-8") as f:
yaml.dump(c, f)
with open(config_file_main, encoding="utf-8") as f:
config = yaml.load(f)
logger.info("Запуск..")
if IN_DOCKER:
logger.info("Обнаружен запуск из DOCKER")
if not os.path.exists(config["help_file"]):
logger.info(f"Создание: {config["help_file"]}...")
with open(config["help_file"], "w", encoding="utf-8") as f:
f.write(raw_help)
def enter_to_exit(exit_code=1):
logger.info("Выход..")
if not IN_DOCKER:
input("\nНажмите Enter для продолжения..")
sys.exit(exit_code)
def new_version():
print("Проверка версии...", end="")
try:
res = requests.get("https://raw.githubusercontent.com/SantaSpeen/Rcon-VK-Bot/master/win/metadata.yml")
data = yaml.load(res.text)
ver = data.get("Version")
if ver:
if int(ver.replace(".", "").replace("-", "")) > int(__version__.replace(".", "").replace("-", "")):
logger.info("Обнаружена новая версия: {} -> {}", __version__, ver)
return True
else:
logger.warning(f"У вас DEV версия: {__version__} (Актуальная: {ver})")
return "DEV"
except Exception as e:
logger.error(f"Не получилось проверить обновления: {e}")
else:
logger.info("У вас актуальная версия")
return False
is_new_version = new_version()
def get_bot():
from .bot import Bot
bot = Bot()
signal.signal(signal.SIGTERM, bot.stop)
signal.signal(signal.SIGINT, bot.stop)
if platform.system() == 'Windows':
signal.signal(signal.SIGBREAK, bot.stop)
elif not IN_DOCKER:
# signal.signal(signal.SIGKILL, bot.stop)
signal.signal(signal.SIGHUP, bot.stop)
return bot

View File

@ -6,9 +6,8 @@ import vk
from easydict import EasyDict
from loguru import logger
import modules
from modules import config, is_new_version
from modules.perms import Permissions
from . import config, is_new_version, enter_to_exit, __version__
from .perms import Permissions
from .hosts import Hosts
@ -28,11 +27,12 @@ class Bot:
def _test(self):
Permissions.perms_file = Path(config["perms_file"])
self.perms = Permissions.load()
Hosts.hosts_file = Path(config["hosts_file"])
self.hosts = Hosts.load()
# Check token
if not config["vk_token"]:
logger.error("Токен ВК не найден.")
modules.enter_to_exit()
enter_to_exit()
def get_lp_server(self):
lp = self.vk.groups.getLongPollServer(group_id=self.group_id)
@ -113,7 +113,7 @@ class Bot:
case "info":
if not message.has_perm(["bot.info"]): return
message.reply(f"RconVkBot\n"
f"Версия бота: {modules.__version__}, последняя: {not is_new_version}")
f"Версия бота: {__version__}, последняя: {not is_new_version}")
case _:
if not message.has_perm(["bot.help"]): return
message.reply(cmds)

51
src/core/history.py Normal file
View File

@ -0,0 +1,51 @@
import time
from datetime import datetime
from threading import Thread
from loguru import logger
from sqlalchemy import create_engine, Column, Integer, DateTime
from sqlalchemy.orm import sessionmaker, declarative_base
Base = declarative_base()
class OnlineStats(Base):
__tablename__ = 'online_stats'
id = Column(Integer, primary_key=True)
timestamp = Column(DateTime, default=datetime.now)
users_online = Column(Integer)
class History:
def __init__(self):
self.hosts = None
self.thread = None
self.stop = False
self.engine = create_engine('sqlite:///./stats.db', echo=False)
Base.metadata.create_all(self.engine)
self.session = sessionmaker(bind=self.engine)()
def _run(self):
while not self.stop:
time.sleep(1)
def load(self, hosts):
self.unload()
self.stop = False
self.hosts = hosts
self.thread = Thread(target=self._run)
self.thread.start()
logger.info("[HOSTS] Поток для отслеживания запущен")
return self
def unload(self, stop=False):
"""Останавливает поток"""
if self.thread:
self.stop = True
self.thread.join()
self.thread = None
if stop:
self.session.close()

View File

@ -6,11 +6,11 @@ from loguru import logger
from mcrcon import MCRcon
from mcstatus import JavaServer, BedrockServer
from modules import yaml, raw_config_hosts, enter_to_exit
from core import yaml, raw_config_hosts, enter_to_exit
class Hosts:
hosts_config = Path("config/hosts.yml")
hosts_file = Path("config/hosts.yml")
def __init__(self, **kwargs):
self._hosts = kwargs["hosts"]
@ -19,6 +19,8 @@ class Hosts:
self._hosts_mine = {}
self._hosts_meta = {}
self._connect()
self.mine = self._hosts_mine
self.meta = self._hosts_meta
logger.info("[HOSTS] Хосты загружены")
def rcon(self, cmd: str, server: str = "default", update=False) -> tuple[str | None, Exception | None]:
@ -122,17 +124,17 @@ class Hosts:
@classmethod
def load(cls) -> "Hosts":
if os.path.exists(cls.hosts_config):
data = yaml.load(cls.hosts_config)
if os.path.exists(cls.hosts_file):
data = yaml.load(cls.hosts_file)
if not data:
os.remove(cls.hosts_config)
os.remove(cls.hosts_file)
return cls.load()
else:
data = yaml.load(raw_config_hosts)
with open(cls.hosts_config, mode="w", encoding="utf-8") as f:
with open(cls.hosts_file, mode="w", encoding="utf-8") as f:
yaml.dump(data, f)
logger.info(f"[HOSTS] {cls.hosts_config} - загружен")
logger.info(f"[HOSTS] {cls.hosts_file} - загружен")
return Hosts(**data)
def unload(self):

View File

@ -3,7 +3,7 @@ from pathlib import Path
from loguru import logger
from modules import yaml, raw_config_perms, enter_to_exit
from core import yaml, raw_config_perms, enter_to_exit
class Permissions:

View File

@ -1,20 +1,9 @@
import platform
import signal
from loguru import logger
from core import Bot
from modules import enter_to_exit, IN_DOCKER
from core import get_bot, enter_to_exit
if __name__ == '__main__':
bot = Bot()
signal.signal(signal.SIGTERM, bot.stop)
signal.signal(signal.SIGINT, bot.stop)
if platform.system() == 'Windows':
signal.signal(signal.SIGBREAK, bot.stop)
elif not IN_DOCKER:
# signal.signal(signal.SIGKILL, bot.stop)
signal.signal(signal.SIGHUP, bot.stop)
bot = get_bot()
try:
bot.listen()
except Exception as e:

View File

@ -1,233 +0,0 @@
import glob
import os
import sys
import zipfile
from datetime import datetime
import requests
from loguru import logger
from ruamel.yaml import YAML
yaml = YAML()
yaml.default_flow_style = False
IN_DOCKER = "IN_DOCKER" in os.environ
__version__ = '2.0.0'
raw_config_main = """\
vk_token: ""
help_file: config/help_message.txt
perms_file: config/permissions.yml
hosts_file: config/hosts.yml
"""
raw_config_perms = """\
noRole: Нет роли
noRights: Нет прав # null для отключения
noNick: Не указан # Используется для !id
# Таблица соответствия vkID к нику в Майнкрафте
# Ник будет передаваться в плагины (Плагины бота)
nicks:
370926160: Rick
583018016: SantaSpeen
perms:
admin: # Имя группы
name: Админ # Имя группы, которое будет отображаться в боте
ids: # вк ИД входящих в состав группы
- 370926160
parent: # Наследование прав
- helper
allow: # Права, подробнее в readme.md
- bot.*
# - bot.help
# - bot.info
# - bot.hosts
# - bot.hosts.*
# - bot.hosts.list
# - bot.hosts.reload
# - bot.perms
# - bot.hosts.*
# - bot.hosts.reload
# - bot.cmd.*
# - bot.cmd.help
# - bot.cmd.id
# - bot.online.*
# - bot.online.default
# - bot.online.lobby
# - bot.history.*
# - bot.history.default
# - bot.history.lobby
# - bot.rcon.*
# - bot.rcon.*.*
helper:
name: Хелпер
ids:
- 583018016
allow:
- bot.rcon.default
- bot.rcon.lobby
- bot.rcon.survival
- bot.rcon.*.say
- bot.rcon.*.mute
- bot.rcon.survival.ban
- bot.rcon.survival.tempban
default:
name: Игрок
allow:
- bot.cmd.help
- bot.cmd.id
- bot.cmd.online.*
- bot.cmd.history.*
"""
raw_config_hosts = """\
hosts:
survival: # Название сервера (имя), может быть любым, может быть сколько угодно
meta:
name: Выживание # Это имя будет выводиться в ответе, или статистике (имя для бота)
java: true # Это JAVA сервер?
important: true # Если да, то бот не включится, если не подключится
# 0 - выключен
# 1 - Доступно без имени (!! Такое значение может быть только у 1 хоста !!) (.rcon <cmd>)
# 2 - Доступно с именем (.rcon <name> <cmd>)
# Разрешение: bot.rcon.<name>; bot.online.<name>; bot.history.<name>
# При запуске бота будет проверка доступности всего
rcon: 2 # RCON будет доступен по команде .rcon lobby <cmd> (разрешение: bot.rcon.lobby)
# !online будет доступен по команде !online lobby (разрешение: bot.cmd.online.lobby)
# !history будет доступен по команде !history lobby (разрешение: bot.cmd.history.lobby)
online: 2
rcon: # RCON подключение
host: 192.168.0.31
port: 15101
password: rconPass1
mine: # Minecraft подключение (нужно для !online и !history)
host: 192.168.0.31
port: 15001
lobby:
meta:
name: Лобби
important: true
java: true
rcon: 1
online: 2
rcon:
host: 192.168.0.31
port: 15100
password: rconPass2
mine:
host: 192.168.0.31
port: 15000
devlobby:
meta:
name: Лобби
important: false
java: true
rcon: 2
online: 2
rcon:
host: 192.168.0.31
port: 15108
password: rconPass3
mine:
host: 192.168.0.31
port: 15008
proxy-local:
meta:
name: Proxy-Local
java: true
important: true
rcon: 0
online: 1
rcon:
mine:
host: 192.168.0.31
port: 15009
"""
raw_help = """\
!help - Вывести это сообщение
!online - Показать текущий онлайн на сервере
"""
config_dir = "./config/"
config_file_main = config_dir + "bot.yml"
if not os.path.exists(config_dir):
os.makedirs(config_dir)
def init_logger():
log_debug = "./logs/debug.log"
log_file = "./logs/latest.log"
log_dir = os.path.dirname(log_file) + "/"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
if os.path.exists(log_file):
ftime = os.path.getmtime(log_file)
zip_path = log_dir + datetime.fromtimestamp(ftime).strftime('%Y-%m-%d') + "-%s.zip"
index = 1
while True:
if not os.path.exists(zip_path % index):
break
index += 1
with zipfile.ZipFile(zip_path % index, "w") as zipf:
logs_files = glob.glob(f"{log_dir}/*.log")
for file in logs_files:
if os.path.exists(file):
zipf.write(file, os.path.basename(file))
os.remove(file)
logger.remove(0)
logger.add(log_debug, level=0, backtrace=True, diagnose=True)
logger.add(log_file, level="INFO", backtrace=False, diagnose=False,
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}")
logger.add(sys.stdout, level="INFO", backtrace=False, diagnose=False,
format="\r<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | {message}")
init_logger()
if not os.path.exists(config_file_main):
logger.info(f"Создание: {config_file_main}...")
c = yaml.load(raw_config_main)
with open(config_file_main, "w") as f:
yaml.dump(c, f)
with open(config_file_main) as f:
config = yaml.load(f)
logger.info("Запуск..")
if IN_DOCKER:
logger.info("Обнаружен запуск из DOCKER")
if not os.path.exists(config["help_file"]):
logger.info(f"Создание: {config["help_file"]}...")
with open(config["help_file"], "w", encoding="utf-8") as f:
f.write(raw_help)
def enter_to_exit(exit_code=1):
logger.info("Выход..")
if not IN_DOCKER:
input("\nНажмите Enter для продолжения..")
sys.exit(exit_code)
def new_version():
print("Проверка версии...", end="")
try:
res = requests.get("https://raw.githubusercontent.com/SantaSpeen/Rcon-VK-Bot/master/win/metadata.yml", timeout=3)
data = yaml.load(res.text)
ver = data.get("Version")
if ver and ver != __version__:
logger.info("Обнаружена новая версия: {} -> {}", __version__, ver)
return True
except Exception as e:
logger.error(f"Не получилось проверить обновления: {e}")
else:
logger.info("У вас актуальная версия")
return False
is_new_version = new_version()

View File

@ -1,20 +0,0 @@
try:
import Bot
import logger
except ImportError:
Bot = object
class Plugin(Bot):
ac_dir = "./perms"
def __init__(self):
super(Plugin, self).__init__()
logger.info(f"Инициализация {self.name} v{self.version}")
async def load(self):
pass
async def unload(self):
pass

View File

@ -1,16 +0,0 @@
LuckPerms:
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
storage-method: PostgreSQL
data:
# Указывайте host:port
address: 127.0.0.1:5432
# База данных в которой хранятся настройки LuckPerms
database: minecraftDB
# Логин и пароль для доступа к БД
username: user
password: user
# Смотрите настройку LuckPerms
table-prefix: luckperms_
server: global

View File

@ -1,61 +0,0 @@
# /cmd cmd: perm - Можно только <perm>
# /cmd cmd: noOne - Команду нельзя использовать из под бота
enabled: false
pluginName: AuthMeReloaded 5.6.0-beta2
data:
/authme register: authme.admin.register
/authme unregister: authme.admin.unregister
/authme forcelogin: authme.admin.forcelogin
/authme password: authme.admin.changepassword
/authme lastlogin: authme.admin.lastlogin
/authme accounts: authme.admin.accounts
/authme email: authme.admin.getemail
/authme setemail: authme.admin.changemail
/authme getip: authme.admin.getip
/authme totp: authme.admin.totpviewstatus
/authme disabletotp: authme.admin.totpdisable
/authme spawn: authme.admin.spawn
/authme setspawn: authme.admin.setspawn
/authme firstspawn: authme.admin.firstspawn
/authme setfirstspawn: authme.admin.setfirstspawn
/authme purge: authme.admin.purge
/authme purgeplayer: authme.admin.purgeplayer
/authme backup: authme.admin.backup
/authme resetpos: authme.admin.purgelastpos
/authme purgebannedplayers: authme.admin.purgebannedplayers
/authme switchantibot: authme.admin.switchantibot
/authme reload: authme.admin.reload
/authme version: authme.admin # Вставил так как стандартно есть у всех
/authme converter: authme.admin.converter
/authme messages: authme.admin.updatemessages
/authme recent: authme.admin.seerecent
/authme debug: authme.debug.command
/authme help: noOne
/email: noOne
/email show: noOne
/email add: noOne
/email change: noOne
/email recover: noOne
/email code: noOne
/email setpassword: noOne
/email help: noOne
/login: noOne
/login help: noOne
/logout: noOne
/logout help: noOne
/register: noOne
/register help: noOne
/unregister: noOne
/unregister help: noOne
/changepassword: noOne
/changepassword help: noOne
/totp: noOne
/totp code: noOne
/totp add: noOne
/totp confirm: noOne
/totp remove: noOne
/totp help: noOne
/captcha: noOne
/captcha help: noOne
/verification: noOne
/verification help: noOne

View File

@ -1,5 +0,0 @@
enabled: false
version: 0.1
name: LuckPerms Integration
dependencies:
- bot >1.3 # Это сам бот

View File

@ -1,4 +0,0 @@
# Поддержка плагинов
Пока ещё в работе