mirror of
https://github.com/SantaSpeen/Rcon-VK-Bot.git
synced 2025-08-18 00:55:51 +00:00
Compare commits
4 Commits
fb7869a64c
...
a56a6506d6
Author | SHA1 | Date | |
---|---|---|---|
a56a6506d6 | |||
6fc868e48d | |||
8a03911814 | |||
b5a982923a |
1
.gitignore
vendored
1
.gitignore
vendored
@ -136,3 +136,4 @@ count*
|
||||
version.txt
|
||||
*.exe
|
||||
logs/
|
||||
config/
|
1
src/core/__init__.py
Normal file
1
src/core/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .bot import Bot
|
188
src/core/bot.py
Normal file
188
src/core/bot.py
Normal file
@ -0,0 +1,188 @@
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
import vk
|
||||
from loguru import logger
|
||||
|
||||
import modules
|
||||
from modules import config, is_new_version
|
||||
from modules.perms import Permissions
|
||||
from .hosts import Hosts
|
||||
|
||||
|
||||
class Bot:
|
||||
|
||||
def __init__(self):
|
||||
self.hosts: Hosts
|
||||
self.perms: Permissions
|
||||
self._test()
|
||||
self.vk = vk.API(access_token=config["vk_token"], v=5.199)
|
||||
print("Получение GroupID..", end="")
|
||||
self.group_id = self.vk.groups.getById()['groups'][0]['id']
|
||||
with open(config["help_file"], encoding="utf-8") as f:
|
||||
self.help_message = f.read()
|
||||
logger.info(f"[BOT] ID группы: {self.group_id}")
|
||||
|
||||
def _test(self):
|
||||
Permissions.perm_file = Path(config["perms_file"])
|
||||
self.perms = Permissions.load()
|
||||
self.hosts = Hosts.load()
|
||||
# Check token
|
||||
if not config["vk_token"]:
|
||||
logger.error("Токен ВК не найден.")
|
||||
modules.enter_to_exit()
|
||||
|
||||
def get_lp_server(self):
|
||||
lp = self.vk.groups.getLongPollServer(group_id=self.group_id)
|
||||
return lp.get('server'), lp.get('key'), lp.get('ts')
|
||||
|
||||
def write(self, peer_id, message):
|
||||
if len(message) > 4095:
|
||||
messages = (len(message) // 4095)
|
||||
for i in range(1, messages + 1):
|
||||
if i > 30:
|
||||
logger.error("[BOT] Сообщение слишком длинное...")
|
||||
return
|
||||
self.write(peer_id, message[:4095 * i])
|
||||
else:
|
||||
self.vk.messages.send(message=message, peer_id=peer_id, random_id=0)
|
||||
|
||||
def _handle_rcon(self, message, _write=True, allow=False):
|
||||
"""Проверка прав и выполнение RCON команды"""
|
||||
from_id = message['from_id']
|
||||
peer_id = message['peer_id']
|
||||
text = message['text']
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
tsplit = text.split(" ")
|
||||
if allow:
|
||||
role = "console"
|
||||
else:
|
||||
if tsplit[1] in self.hosts.hosts:
|
||||
props = {"cmd": " ".join(tsplit[2:]), "server": tsplit[1]}
|
||||
else:
|
||||
props = {"cmd": " ".join(tsplit[1:])}
|
||||
allow, role = self.perms.is_allowed(from_id, props['cmd'])
|
||||
if allow:
|
||||
answer, _ = self.hosts.rcon(**props)
|
||||
if not answer:
|
||||
answer = "Выполнено без ответа."
|
||||
logger.info(f"[BOT] User: {from_id}({role}) in Chat: {peer_id} use RCON cmd: \"{props['cmd']}\", "
|
||||
f"with answer: \"{answer}\"")
|
||||
if _write:
|
||||
self.write(peer_id, ("" if not props.get("server") else f"Ответ от {self.hosts._hosts_meta[props["server"]].get("name", props["server"])}:\n") + answer)
|
||||
else:
|
||||
return answer
|
||||
else:
|
||||
logger.info(f"[BOT] User: {from_id}({role}) in Chat: {peer_id} no have rights RCON cmd: \"{props['cmd']}\".")
|
||||
if self.perms.no_rights: # Если есть текст
|
||||
self.write(peer_id, self.perms.no_rights)
|
||||
|
||||
def _handle_bot(self, message):
|
||||
from_id = message['from_id']
|
||||
if self.perms.is_allowed(from_id, "bot"):
|
||||
peer_id = message['peer_id']
|
||||
text = message['text']
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
tsplit = text.split(" ")
|
||||
cmds = ("Доступные команды:\n"
|
||||
" .bot help - Вывести это сообщение.\n"
|
||||
" .bot info - Выводит краткую информацию о боте.\n"
|
||||
" .bot hosts list - Список доступных хостов\n"
|
||||
" .bot hosts reload - Перезагружает hosts.yml\n"
|
||||
# " .bot perms user [add | del] <group> - не реализовано \n"
|
||||
# " .bot perms list - Выводит список групп \n"
|
||||
" .bot perms reload - Перезагружает permissions.yml")
|
||||
if len(tsplit) == 1:
|
||||
self.write(peer_id, cmds)
|
||||
return
|
||||
match tsplit[1]:
|
||||
case "hosts":
|
||||
match tsplit[2] if len(tsplit) > 2 else None:
|
||||
case "list":
|
||||
s = ""
|
||||
for host in self.hosts.hosts:
|
||||
ping = 0
|
||||
r, e = self.hosts.mine(host, True)
|
||||
if not e:
|
||||
ping = r.latency
|
||||
_, e = self.hosts.rcon("list", host, True)
|
||||
meta = self.hosts._hosts_meta[host]
|
||||
name = meta.get("name")
|
||||
rcon_ok = meta.get('rcon_ok')
|
||||
mine_ok = meta.get('mine_ok')
|
||||
if (not rcon_ok and meta['rcon'] > 0) or (not mine_ok and meta['online'] > 0):
|
||||
s += f"\nㅤ⛔ {host} ({name})"
|
||||
else:
|
||||
s += f"\nㅤ✅ {host} ({name})"
|
||||
if "-a" in tsplit or "--all" in tsplit:
|
||||
# noinspection SpellCheckingInspection
|
||||
s += (f":\n"
|
||||
f"ㅤㅤimportant: {meta['important']}\n"
|
||||
f"ㅤㅤrcon_default: {meta['rcon'] == 1}\n"
|
||||
f"ㅤㅤmine_default: {meta['online'] == 1}\n"
|
||||
f"ㅤㅤrcon: {not bool(e)}\n"
|
||||
f"ㅤㅤping: {ping:.4f}ms\n"
|
||||
f"ㅤㅤrcon_ok: {rcon_ok}\n"
|
||||
f"ㅤㅤmine_ok: {mine_ok}")
|
||||
self.write(peer_id, "Список хостов:" + s)
|
||||
case "reload":
|
||||
self.hosts = Hosts.load()
|
||||
self.write(peer_id, "hosts.yml - Загружен")
|
||||
case _:
|
||||
self.write(peer_id, ".bot hosts [list | reload]")
|
||||
case "perms":
|
||||
match tsplit[2] if len(tsplit) > 2 else None:
|
||||
case "reload":
|
||||
self.perms = Permissions.load()
|
||||
self.write(peer_id, "permissions.yml - Загружен")
|
||||
case _:
|
||||
self.write(peer_id, ".bot perms [reload]")
|
||||
case "info":
|
||||
self.write(peer_id, f"RconVkBot\n"
|
||||
f"Версия бота: {modules.__version__}, последняя: {not is_new_version}")
|
||||
case _:
|
||||
self.write(peer_id, cmds)
|
||||
|
||||
def message_handle(self, message):
|
||||
from_id = message['from_id']
|
||||
peer_id = message['peer_id']
|
||||
text = message['text']
|
||||
match text:
|
||||
case i if i.startswith(".rcon "):
|
||||
self._handle_rcon(message)
|
||||
case i if i.startswith(".bot"):
|
||||
self._handle_bot(message)
|
||||
case "!help":
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
self.write(peer_id, self.help_message)
|
||||
case "!online":
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
server, _ = self.hosts.mine()
|
||||
players = server.players
|
||||
self.write(peer_id, f"На сервере сейчас {players.online}/{players.max}")
|
||||
case "!id":
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
self.write(peer_id,
|
||||
f"Твой ID: {from_id}\n"
|
||||
f"Роль: {self.perms.get_role(from_id)}\n"
|
||||
f"Ник: {self.perms.get_nick(from_id)}")
|
||||
|
||||
def listen(self):
|
||||
server, key, ts = self.get_lp_server()
|
||||
logger.info("[BOT] Начинаю получать сообщения..")
|
||||
while True:
|
||||
lp = requests.get(f'{server}?act=a_check&key={key}&ts={ts}&wait=25').json()
|
||||
try:
|
||||
if lp.get('failed') is not None:
|
||||
key = self.get_lp_server()[1]
|
||||
if ts != lp.get('ts') and lp.get('updates'):
|
||||
updates = lp['updates'][0]
|
||||
if updates['type'] == "message_new":
|
||||
self.message_handle(updates['object']['message'])
|
||||
ts = lp.get('ts')
|
||||
except Exception as i:
|
||||
ts = lp.get('ts')
|
||||
logger.exception(i)
|
||||
|
||||
def stop(self):
|
||||
self.hosts.unload()
|
124
src/core/hosts.py
Normal file
124
src/core/hosts.py
Normal file
@ -0,0 +1,124 @@
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
from mcrcon import MCRcon
|
||||
from mcstatus import JavaServer, BedrockServer
|
||||
|
||||
from modules import yaml, raw_config_hosts, enter_to_exit
|
||||
|
||||
|
||||
class Hosts:
|
||||
hosts_config = Path("config/hosts.yml")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._hosts = kwargs["hosts"]
|
||||
self.hosts = set()
|
||||
self._hosts_rcon = {}
|
||||
self._hosts_mine = {}
|
||||
self._hosts_meta = {}
|
||||
self._connect()
|
||||
logger.info("[HOSTS] Хосты загружены")
|
||||
|
||||
def rcon(self, cmd: str, server: str = "default", update=False) -> tuple[str | None, Exception | None]:
|
||||
if not update and not self._hosts_meta[server].get('rcon_ok', True):
|
||||
return ("Сервер в листе нерабочих.\nЕсли это не так, обновить можно этой командой\n.bot hosts list",
|
||||
Exception("Сервер в листе нерабочих"))
|
||||
if server not in self._hosts_rcon:
|
||||
return "Сервер не найден", Exception("Сервер не найден")
|
||||
try:
|
||||
with MCRcon(**self._hosts_rcon[server]) as mcr:
|
||||
text = mcr.command(cmd)
|
||||
self._hosts_meta[server]['rcon_ok'] = True
|
||||
return re.sub(r'§.', '', text), None
|
||||
except Exception as e:
|
||||
self._hosts_meta[server]['rcon_ok'] = False
|
||||
logger.error(f"[RCON] Сервер: {server}; Команда: {cmd}")
|
||||
logger.error(e)
|
||||
return f"error: \"{e}\"", e
|
||||
|
||||
def mine(self, server: str = "default", update=False) -> tuple[JavaServer.lookup, Exception | None]:
|
||||
if not update and not self._hosts_meta[server].get('mine_ok', True):
|
||||
return ("Сервер в листе нерабочих.\nЕсли это не так, обновить можно этой командой\n.bot hosts list",
|
||||
Exception("Сервер в листе нерабочих"))
|
||||
if server not in self._hosts_mine:
|
||||
return None, Exception("Сервер не найден")
|
||||
s = self._hosts_mine[server]
|
||||
try:
|
||||
if self._hosts_meta[server]["java"]:
|
||||
srv = JavaServer.lookup(f'{s["host"]}:{s["port"]}').status()
|
||||
else:
|
||||
# noinspection PyArgumentList
|
||||
srv = BedrockServer.lookup(f'{s["host"]}:{s["port"]}').status()
|
||||
self._hosts_meta[server]['mine_ok'] = True
|
||||
return srv, None
|
||||
except Exception as e:
|
||||
self._hosts_meta[server]['mine_ok'] = False
|
||||
logger.error(f"[MINE] Сервер не отвечает: {server} {s}")
|
||||
logger.exception(e)
|
||||
self._hosts_meta["connected"] = False
|
||||
return None, e
|
||||
|
||||
def _connect(self) -> None:
|
||||
if self._hosts is None or len(self._hosts) == 0:
|
||||
logger.error("[HOSTS] Не найдено ни одного хоста.")
|
||||
enter_to_exit()
|
||||
|
||||
self._hosts_meta['default'] = {}
|
||||
|
||||
# Test RCON
|
||||
for name in self._hosts:
|
||||
self.hosts.add(name)
|
||||
server = self._hosts[name]
|
||||
meta = self._hosts_meta[name] = server['meta']
|
||||
if meta['rcon'] > 0:
|
||||
rcon = self._hosts_rcon[name] = server['rcon']
|
||||
print(f"Проверка RCON {name}..", end="")
|
||||
srv, e = self.rcon("list", name)
|
||||
if srv:
|
||||
if meta['rcon'] == 1:
|
||||
if self._hosts_rcon.get('default'):
|
||||
logger.warning(f"[RCON] hosts.{name}.meta.rcon = 1 - Хотя уже есть дефолтный.")
|
||||
self._hosts_rcon['default'] = rcon
|
||||
self._hosts_meta['default']['rcon_ok'] = True
|
||||
logger.info(f"[RCON] {name} доступен.")
|
||||
else:
|
||||
if meta["important"]:
|
||||
logger.error(f"[RCON] Важный хост не доступен: {name}")
|
||||
enter_to_exit()
|
||||
if meta["online"] > 0:
|
||||
mine = self._hosts_mine[name] = server['mine']
|
||||
print(f"Проверка MINE {name}..", end="")
|
||||
srv, e = self.mine(name)
|
||||
if srv:
|
||||
if meta['online'] == 1:
|
||||
if self._hosts_mine.get('default'):
|
||||
logger.warning(f"[RCON] hosts.{name}.meta.online = 1 - Хотя уже есть дефолтный.")
|
||||
self._hosts_mine['default'] = mine
|
||||
self._hosts_meta['default']['java'] = meta['java']
|
||||
self._hosts_meta['default']['mine_ok'] = True
|
||||
players = srv.players
|
||||
logger.info(f"[MINE] {name} доступен. {players.online}/{players.max} {srv.latency:.3f}ms")
|
||||
else:
|
||||
if meta["important"]:
|
||||
logger.error(f"[MINE] Важный хост не доступен: {name}")
|
||||
enter_to_exit()
|
||||
|
||||
@classmethod
|
||||
def load(cls) -> "Hosts":
|
||||
if os.path.exists(cls.hosts_config):
|
||||
data = yaml.load(cls.hosts_config)
|
||||
if not data:
|
||||
os.remove(cls.hosts_config)
|
||||
return cls.load()
|
||||
else:
|
||||
data = yaml.load(raw_config_hosts)
|
||||
with open(cls.hosts_config, mode="w", encoding="utf-8") as f:
|
||||
yaml.dump(data, f)
|
||||
|
||||
logger.info(f"[HOSTS] {cls.hosts_config} - загружен")
|
||||
return Hosts(**data)
|
||||
|
||||
def unload(self):
|
||||
return
|
33
src/main.py
33
src/main.py
@ -1,40 +1,19 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from modules import config, rcon, get_server_status, enter_to_exit
|
||||
from modules.bot import Bot
|
||||
from modules.perms import Permissions
|
||||
from core import Bot
|
||||
from modules import enter_to_exit
|
||||
|
||||
if __name__ == '__main__':
|
||||
Permissions.perm_file = Path(config.permissions_file)
|
||||
perms = Permissions.load()
|
||||
# Check token
|
||||
if not config.vk.token:
|
||||
logger.error("Токен ВК не найден.")
|
||||
enter_to_exit()
|
||||
bot = Bot(perms)
|
||||
# Test RCON
|
||||
print("Проверка RCON..", end="")
|
||||
if rcon("list").startswith("Rcon error"):
|
||||
logger.error("RCON не отвечает. Проверьте блок \"rcon\" в config.json")
|
||||
enter_to_exit()
|
||||
logger.info("RCON доступен.")
|
||||
# Test Minecraft Server
|
||||
print("Проверка сервера..", end="")
|
||||
try:
|
||||
st = get_server_status()
|
||||
players = st.players
|
||||
logger.info(f"Сервер доступен. Пинг: {st.latency:.3f}ms, Онлайн: {players.online}/{players.max}")
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
logger.info("Сервер не отвечает. Проверьте блок \"minecraft\" в config.json")
|
||||
enter_to_exit()
|
||||
bot = Bot()
|
||||
try:
|
||||
bot.listen()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Exited.")
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
enter_to_exit()
|
||||
finally:
|
||||
bot.stop()
|
||||
|
@ -1,15 +1,11 @@
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
from loguru import logger
|
||||
from mcrcon import MCRcon
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
yaml = YAML()
|
||||
@ -18,24 +14,118 @@ IN_DOCKER = "DOCKER_CONTAINER" in os.environ
|
||||
|
||||
__version__ = '1.3.1'
|
||||
|
||||
raw_config = """\
|
||||
{
|
||||
"vk": {
|
||||
"token": "",
|
||||
"help_file": "help_message.txt"
|
||||
},
|
||||
"rcon": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 25575,
|
||||
"password": "P@ssw0rd"
|
||||
},
|
||||
"minecraft": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 25565,
|
||||
"java": true
|
||||
},
|
||||
"permissions_file": "permissions.yml"
|
||||
}"""
|
||||
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: # Какие команды разрешены, "*" - все
|
||||
- '*'
|
||||
helper:
|
||||
name: Хелпер
|
||||
ids:
|
||||
- 583018016
|
||||
allow:
|
||||
- bot.rcon.* # См. host.yml
|
||||
- say
|
||||
- mute
|
||||
- warn
|
||||
default:
|
||||
name: Игрок
|
||||
allow:
|
||||
- bot.online.* # См. host.yml
|
||||
- bot.history.* # См. host.yml
|
||||
|
||||
"""
|
||||
|
||||
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: 2 # !online будет доступен по команде !online lobby (разрешение: bot.online.lobby)
|
||||
history: 2 # !history будет доступен по команде !history lobby (разрешение: bot.history.lobby)
|
||||
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
|
||||
history: 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
|
||||
history: 0
|
||||
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
|
||||
history: 1
|
||||
rcon: null
|
||||
mine:
|
||||
host: 192.168.0.31
|
||||
port: 15009
|
||||
"""
|
||||
|
||||
raw_help = """\
|
||||
Тебе не нужна помощь, ты и так беспомощный, кожаный ублюдок. Так уж и быть, подскажу пару команд...
|
||||
@ -44,6 +134,11 @@ raw_help = """\
|
||||
Бот сделан кожанным петухом - админом, все вопросы к нему, я не причём.
|
||||
"""
|
||||
|
||||
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_file = "./logs/latest.log"
|
||||
@ -71,45 +166,21 @@ def init_logger():
|
||||
|
||||
|
||||
init_logger()
|
||||
if not os.path.exists("config.json"):
|
||||
logger.info("Создание: config.json...")
|
||||
with open("config.json", "w") as f:
|
||||
f.write(raw_config)
|
||||
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.json') as f:
|
||||
config = json.load(f, object_hook=lambda x: namedtuple('X', x.keys())(*x.values()))
|
||||
with open(config_file_main) as f:
|
||||
config = yaml.load(f)
|
||||
|
||||
logger.info("Запуск..")
|
||||
if not os.path.exists(config.vk.help_file):
|
||||
logger.info(f"Создание: {config.vk.help_file}...")
|
||||
if not os.path.exists(config["help_file"]):
|
||||
logger.info(f"Создание: {config["help_file"]}...")
|
||||
with open(config.vk.help_file, "w", encoding="utf-8") as f:
|
||||
f.write(raw_help)
|
||||
|
||||
if config.minecraft.java:
|
||||
from mcstatus import JavaServer as MineServer
|
||||
else:
|
||||
from mcstatus import BedrockServer as MineServer
|
||||
|
||||
host = config.rcon.host
|
||||
port = config.rcon.port
|
||||
password = config.rcon.password
|
||||
|
||||
|
||||
def rcon(cmd):
|
||||
try:
|
||||
with MCRcon(host, password, port) as mcr:
|
||||
text = mcr.command(cmd)
|
||||
return re.sub(r'§.', '', text)
|
||||
except Exception as e:
|
||||
logger.error(f"[RCON] ERROR with command: {cmd}")
|
||||
logger.exception(e)
|
||||
return f"Rcon error: {e}"
|
||||
|
||||
|
||||
def get_server_status():
|
||||
server = MineServer.lookup(config.minecraft.host, config.minecraft.port)
|
||||
return server.status()
|
||||
|
||||
|
||||
def enter_to_exit(exit_code=1):
|
||||
logger.info("Выход..")
|
||||
@ -127,8 +198,8 @@ def new_version():
|
||||
if ver and ver != __version__:
|
||||
logger.info("Обнаружена новая версия: {} -> {}", __version__, ver)
|
||||
return True
|
||||
except:
|
||||
logger.error("Не получилось проверить обновления.")
|
||||
except Exception as e:
|
||||
logger.error(f"Не получилось проверить обновления: {e}")
|
||||
else:
|
||||
logger.info("У вас актуальная версия")
|
||||
return False
|
||||
|
@ -1,121 +0,0 @@
|
||||
import requests
|
||||
import vk
|
||||
from loguru import logger
|
||||
|
||||
import modules
|
||||
from modules import config, rcon, get_server_status, is_new_version
|
||||
from modules.perms import Permissions
|
||||
|
||||
|
||||
class Bot:
|
||||
|
||||
def __init__(self, perms: Permissions):
|
||||
self.perms: Permissions = perms
|
||||
self.vk = vk.API(access_token=config.vk.token, v=5.199)
|
||||
print("Получение GroupID..", end="")
|
||||
self.group_id = self.vk.groups.getById()['groups'][0]['id']
|
||||
with open('help_message.txt', encoding="utf-8") as f:
|
||||
self.help_message = f.read()
|
||||
logger.info(f"[BOT] ID группы: {self.group_id}")
|
||||
|
||||
def get_lp_server(self):
|
||||
lp = self.vk.groups.getLongPollServer(group_id=self.group_id)
|
||||
return lp.get('server'), lp.get('key'), lp.get('ts')
|
||||
|
||||
def write(self, peer_id, message):
|
||||
if len(message) > 4095:
|
||||
messages = (len(message) // 4095)
|
||||
for i in range(1, messages + 1):
|
||||
if i > 30:
|
||||
logger.error("[BOT] Сообщение слишком длинное...")
|
||||
break
|
||||
self.write(peer_id, message[:4095 * i])
|
||||
else:
|
||||
self.vk.messages.send(message=message, peer_id=peer_id, random_id=0)
|
||||
|
||||
def rcon_cmd_handle(self, cmd, from_id, peer_id, _write=True, _allow=False):
|
||||
a, r = self.perms.is_allowed(from_id, cmd.split()[0])
|
||||
if _allow:
|
||||
r = cmd
|
||||
if a or _allow:
|
||||
answer = rcon(cmd)
|
||||
logger.info(f"[BOT] User: {from_id}({r}) in Chat: {peer_id} use RCON cmd: \"{cmd}\", "
|
||||
f"with answer: \"{answer}\"")
|
||||
if _write:
|
||||
self.write(peer_id, f"RCON:\n{answer}")
|
||||
else:
|
||||
return answer
|
||||
else:
|
||||
logger.info(f"[BOT] User: {from_id}({r}) in Chat: {peer_id} no have rights RCON cmd: \"{cmd}\".")
|
||||
|
||||
def _bot_handle(self, message):
|
||||
from_id = message['from_id']
|
||||
if self.perms.is_allowed(from_id, "bot"):
|
||||
peer_id = message['peer_id']
|
||||
text = message['text']
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
tsplit = text.split(" ")
|
||||
cmds = ("Доступные команды:\n"
|
||||
" .bot help - Вывести это сообщение.\n"
|
||||
" .bot info - Выводит краткую информацию о боте.\n"
|
||||
" .bot perm user <> - Добавить пользователя \n"
|
||||
" .bot perm reload - Перезагружает пермишины")
|
||||
if len(tsplit) == 1:
|
||||
self.write(peer_id, cmds)
|
||||
return
|
||||
match tsplit[1]:
|
||||
case "perm":
|
||||
match tsplit[2] if len(tsplit) > 2 else None:
|
||||
case "reload":
|
||||
self.perms = Permissions.load()
|
||||
self.write(peer_id, "Права перезагружены")
|
||||
case _:
|
||||
self.write(peer_id, ".bot perm ?")
|
||||
case "info":
|
||||
st = get_server_status()
|
||||
self.write(peer_id, f"RconVkBot\n"
|
||||
f"Версия бота: {modules.__version__}, последняя: {not is_new_version}\n"
|
||||
f"Пинг до сервера: {st.latency:.3f}ms\n"
|
||||
f"Бедрок: {st.motd.bedrock} ({st.version.protocol})\n")
|
||||
case _:
|
||||
self.write(peer_id, cmds)
|
||||
|
||||
def message_handle(self, message):
|
||||
from_id = message['from_id']
|
||||
peer_id = message['peer_id']
|
||||
text = message['text']
|
||||
match text:
|
||||
case i if i.startswith(".rcon "):
|
||||
self.rcon_cmd_handle(i[6:], from_id, peer_id)
|
||||
case i if i.startswith(".bot"):
|
||||
self._bot_handle(message)
|
||||
case "!help":
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
self.write(peer_id, self.help_message)
|
||||
case "!online":
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
players = get_server_status().players
|
||||
self.write(peer_id, f"На сервере сейчас {players.online}/{players.max}")
|
||||
case "!id":
|
||||
logger.info(f"[BOT] {peer_id}:{from_id}:{text}")
|
||||
self.write(peer_id,
|
||||
f"Твой ID: {from_id}\n"
|
||||
f"Роль: {self.perms.get_role(from_id)}\n"
|
||||
f"Ник: {self.perms.get_nick(from_id)}")
|
||||
|
||||
def listen(self):
|
||||
server, key, ts = self.get_lp_server()
|
||||
logger.info("[BOT] Начинаю получать сообщения..")
|
||||
while True:
|
||||
lp = requests.get(f'{server}?act=a_check&key={key}&ts={ts}&wait=25').json()
|
||||
try:
|
||||
if lp.get('failed') is not None:
|
||||
key = self.get_lp_server()[1]
|
||||
if ts != lp.get('ts') and lp.get('updates'):
|
||||
updates = lp['updates'][0]
|
||||
if updates['type'] == "message_new":
|
||||
self.message_handle(updates['object']['message'])
|
||||
ts = lp.get('ts')
|
||||
except Exception as i:
|
||||
ts = lp.get('ts')
|
||||
logger.exception(i)
|
@ -1,10 +1,9 @@
|
||||
import os.path
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from modules import yaml
|
||||
from modules import yaml, raw_config_perms
|
||||
|
||||
|
||||
class Permissions:
|
||||
@ -15,14 +14,10 @@ class Permissions:
|
||||
self._no_nick = kwargs.get("noNick")
|
||||
self.no_rights = kwargs.get("noRights")
|
||||
self._perms = kwargs['perms']
|
||||
self._nicks = kwargs['nicks']
|
||||
self._members = {}
|
||||
if kwargs['useLuckPerms']:
|
||||
logger.info("[PERMS] Поддержка LuckPerms всё ещё в разработке. ")
|
||||
print(kwargs['LuckPerms']['nicks'])
|
||||
sys.exit(1)
|
||||
self._luck_perms = kwargs['LuckPerms']
|
||||
logger.info(f"[PERMS] {self.perm_file} - загружен")
|
||||
self.__handle_members()
|
||||
logger.info(f"[PERMS] Права загружены")
|
||||
|
||||
def __handle_members(self):
|
||||
for role, role_data in self._perms.items():
|
||||
@ -37,7 +32,7 @@ class Permissions:
|
||||
self._members[member] = {
|
||||
"role": role,
|
||||
"friendly": role_data.get("name", role),
|
||||
"nick": self._luck_perms['nicks'].get(member) or self._no_nick,
|
||||
"nick": self._nicks.get(member) or self._no_nick,
|
||||
"allow": allow
|
||||
}
|
||||
|
||||
@ -71,59 +66,12 @@ class Permissions:
|
||||
os.remove(cls.perm_file)
|
||||
return Permissions.load()
|
||||
else:
|
||||
logger.info(f"Generating permissions file: {cls.perm_file}")
|
||||
import textwrap
|
||||
raw = textwrap.dedent("""\
|
||||
noRole: Нет роли
|
||||
noRights: Нет прав # null для отключения
|
||||
noNick: Не указан # Используется для !id, ник берётся из LuckPerms.nicks независимо от useLuckPerms
|
||||
perms:
|
||||
admins: # Имя группы
|
||||
name: Админ # Имя группы, которое будет отображаться в боте
|
||||
ids: # вк ИД входящих в состав группы
|
||||
- 370926160
|
||||
allow: # Какие команды разрешены, "*" - все
|
||||
- '*'
|
||||
# Пример настройки
|
||||
helpers:
|
||||
name: Хелпер
|
||||
ids:
|
||||
- 583018016
|
||||
allow:
|
||||
- say
|
||||
- mute
|
||||
- warn
|
||||
|
||||
# Находится в режиме тестирования
|
||||
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
|
||||
useLuckPerms: false
|
||||
LuckPerms:
|
||||
|
||||
# Таблица соответствия vkID к нику в Майнкрафте
|
||||
nicks:
|
||||
370926160: Rick
|
||||
583018016: SantaSpeen
|
||||
|
||||
# Разрешенные варианты: 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
|
||||
|
||||
""")
|
||||
data = yaml.load(raw)
|
||||
logger.info(f"Создание: {cls.perm_file}...")
|
||||
data = yaml.load(raw_config_perms)
|
||||
with open(cls.perm_file, mode="w", encoding="utf-8") as f:
|
||||
yaml.dump(data, f)
|
||||
|
||||
logger.info(f"[PERMS] {cls.perm_file} - загружен")
|
||||
return Permissions(**data)
|
||||
|
||||
|
||||
|
0
src/modules/plugins_loader.py
Normal file
0
src/modules/plugins_loader.py
Normal file
20
src/plugins/LuckPerms/LuckPerms.py
Normal file
20
src/plugins/LuckPerms/LuckPerms.py
Normal file
@ -0,0 +1,20 @@
|
||||
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
|
16
src/plugins/LuckPerms/config.yml
Normal file
16
src/plugins/LuckPerms/config.yml
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
61
src/plugins/LuckPerms/perms/authme.yml
Normal file
61
src/plugins/LuckPerms/perms/authme.yml
Normal file
@ -0,0 +1,61 @@
|
||||
# /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
|
5
src/plugins/LuckPerms/plugin.yml
Normal file
5
src/plugins/LuckPerms/plugin.yml
Normal file
@ -0,0 +1,5 @@
|
||||
enabled: false
|
||||
version: 0.1
|
||||
name: LuckPerms Integration
|
||||
dependencies:
|
||||
- bot >1.3 # Это сам бот
|
4
src/plugins/readme.md
Normal file
4
src/plugins/readme.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Поддержка плагинов
|
||||
|
||||
Пока ещё в работе
|
||||
|
Loading…
x
Reference in New Issue
Block a user