Compare commits

...

7 Commits

Author SHA1 Message Date
f2aba85982 [~] ... 2024-01-13 12:31:05 +03:00
a5b052e885 [~] Critical bugfix 2024-01-13 12:28:39 +03:00
9325621963 [~] ping in server check
[~] Bot to modules/bot.py
[~] 1.3.0 -> 1.3.1
2024-01-13 12:14:12 +03:00
dfccc91224 [+] Version checker
[+] cmd .bot info
[+] [perms] get_nick
2024-01-13 11:52:57 +03:00
69ea9343da [~] Update 2024-01-13 11:51:31 +03:00
2ef8d57836 [~] Update 2024-01-13 09:35:52 +03:00
d7135db073 [~] encoding
[!] Critical bugfix
[+] cmd `.bot`
[+] cmd `.bot perm reload`
[+] LuckPerm.nick
2024-01-13 09:35:44 +03:00
6 changed files with 209 additions and 121 deletions

View File

@ -10,6 +10,7 @@
* Доступные разрешённым людям
* **`.rcon <command>`** - Исполняет <*command*> и показывает ответ сервера
* **`.bot`** - Команды бота, требует разрешения `bot`
* Доступные всем
* **`!help`** - Выводит страничку с командами (Текст в файле help_message.txt)
* **`!online`** - Запрашивает у сервера онлайн и выводит
@ -41,11 +42,12 @@ _Всё очень легко и просто)_
## Система permissions
В файле `permissions.yml` указаны все пользователи с "повышенным" уровнем доступа к боту
В файле `permissions.yml` указаны все пользователи с "повышенным" уровнем доступа к боту\
Пример
```yaml
noRole: Нет роли
noRights: Нет прав # null для отключения
noNick: Не указан # Используется для !id, ник берётся из LuckPerms.nicks независимо от useLuckPerms
perms:
admins: # Имя группы
name: Админ # Имя группы, которое будет отображаться в боте
@ -67,6 +69,12 @@ perms:
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
useLuckPerms: false
LuckPerms:
# Таблица соответствия vkID к нику в Майнкрафте
nicks:
370926160: Rick
583018016: SantaSpeen
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
storage-method: PostgreSQL
data:
@ -79,11 +87,11 @@ LuckPerms:
password: user
# Смотрите настройку LuckPerms
server: global
table-prefix: luckperms_
server: global
```
LuckPerms
**Интеграция с LuckPerms ещё не готова!**
### За помощью, заказами и предложениями можно обратиться сюда:

View File

@ -1,112 +1,40 @@
import sys
import traceback
from pathlib import Path
import requests
import vk
from loguru import logger
from modules import config, rcon, perms, get_server_status, enter_to_exit
class Bot:
def __init__(self):
self.vk = vk.API(access_token=config.vk.token, v=5.199)
self.group_id = self.vk.groups.getById()['groups'][0]['id']
with open('help_message.txt') 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.info("[BOT] Сообщение слишком длинное...")
break
self.write(peer_id, message[:4095 * i])
else:
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 = 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 message_handle(self, message):
from_id = message['from_id']
peer_id = message['peer_id']
text = message['text']
match text:
case i if i.startwith(".rcon "):
self.rcon_cmd_handle(i[6:], from_id, peer_id)
case "!help":
self.write(peer_id, self.help_message)
case "!online":
online = get_server_status().online
self.write(peer_id, f"На сервере сейчас {online}")
case "!id":
self.write(peer_id, f"Твой ID: {from_id}\nРоль: {perms.get_role(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 KeyboardInterrupt:
raise KeyboardInterrupt
except Exception as i:
ts = lp.get('ts')
logger.exception(i)
from modules import config, rcon, get_server_status, enter_to_exit
from modules.bot import Bot
from modules.perms import Permissions
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()
try:
bot = Bot()
# Test RCON
if rcon("list").startswith("Rcon error"):
logger.error("RCON не отвечает. Проверьте блок \"rcon\" в config.json")
enter_to_exit()
logger.info("RCON доступен.")
try:
# Test Minecraft Server
players = get_server_status().players
logger.info(f"Проверка сервера. Онлайн: {players.online}/{players.max}")
except Exception as e:
logger.exception(e)
logger.info("Сервер не отвечает. Проверьте блок \"minecraft\" в config.json")
enter_to_exit()
bot.listen()
except KeyboardInterrupt:
pass
sys.exit(0)
except Exception as e:
logger.exception(e)
enter_to_exit()

View File

@ -3,16 +3,20 @@ import json
import os
import re
import sys
import traceback
import zipfile
from collections import namedtuple
from datetime import datetime
from pathlib import Path
import requests
from loguru import logger
from mcrcon import MCRcon
from ruamel.yaml import YAML
from .perms import Permissions
yaml = YAML()
yaml.default_flow_style = False
__version__ = '1.3.1'
raw_config = """\
{
@ -41,7 +45,7 @@ raw_help = """\
"""
def zip_logs():
def init_logger():
log_file = "./logs/latest.log"
log_dir = os.path.dirname(log_file) + "/"
if not os.path.exists(log_dir):
@ -62,11 +66,11 @@ def zip_logs():
os.remove(file)
logger.remove(0)
logger.add(log_file)
logger.add(sys.stdout, format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
logger.add(sys.stdout, format="\r<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | {message}")
zip_logs()
init_logger()
if not os.path.exists("config.json"):
logger.info("Создание: config.json...")
with open("config.json", "w") as f:
@ -107,11 +111,25 @@ def get_server_status():
return server.status()
Permissions.perm_file = Path(config.permissions_file)
perms = Permissions.load()
def enter_to_exit():
def enter_to_exit(exit_code=1):
logger.info("Выход..")
input("\nНажмите Enter для продолжения..")
sys.exit(1)
sys.exit(exit_code)
def new_version():
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 and ver != __version__:
logger.info("Обнаружена новая версия: {} -> {}", __version__, ver)
return True
except:
logger.error("Не получилось проверить обновления.")
else:
logger.info("У вас актуальная версия")
return False
is_new_version = new_version()

121
src/modules/bot.py Normal file
View File

@ -0,0 +1,121 @@
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)

View File

@ -3,10 +3,8 @@ import sys
from pathlib import Path
from loguru import logger
from ruamel.yaml import YAML
yaml = YAML()
yaml.default_flow_style = False
from modules import yaml
class Permissions:
@ -14,12 +12,13 @@ class Permissions:
def __init__(self, **kwargs):
self._no_role = kwargs.get("noRole")
self._no_nick = kwargs.get("noNick")
self.no_rights = kwargs.get("noRights")
self._perms = kwargs['perms']
self._members = {}
if kwargs['useLuckPerms']:
logger.info("[PERMS] Using LuckPerms mode")
logger.info("[PERMS] LuckPerms mode support still in development")
logger.info("[PERMS] Поддержка LuckPerms всё ещё в разработке. ")
print(kwargs['LuckPerms']['nicks'])
sys.exit(1)
self._luck_perms = kwargs['LuckPerms']
logger.info(f"[PERMS] {self.perm_file} - загружен")
@ -38,6 +37,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,
"allow": allow
}
@ -57,6 +57,12 @@ class Permissions:
return u['friendly']
return self._no_role
def get_nick(self, member):
u = self._members.get(member)
if u:
return u.get("nick")
return self._no_nick
@classmethod
def load(cls):
if os.path.exists(cls.perm_file):
@ -70,6 +76,7 @@ class Permissions:
raw = textwrap.dedent("""\
noRole: Нет роли
noRights: Нет прав # null для отключения
noNick: Не указан # Используется для !id, ник берётся из LuckPerms.nicks независимо от useLuckPerms
perms:
admins: # Имя группы
name: Админ # Имя группы, которое будет отображаться в боте
@ -86,12 +93,17 @@ class Permissions:
- say
- mute
- warn
# Находится в режиме тестирования
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
useLuckPerms: false
LuckPerms:
# Таблица соответствия vkID к нику в Майнкрафте
nicks:
370926160: Rick
583018016: SantaSpeen
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
storage-method: PostgreSQL
data:
@ -102,10 +114,11 @@ class Permissions:
# Логин и пароль для доступа к БД
username: user
password: user
# Смотрите настройку LuckPerms
server: global
table-prefix: luckperms_
server: global
""")
data = yaml.load(raw)
with open(cls.perm_file, mode="w", encoding="utf-8") as f:

View File

@ -1,6 +1,6 @@
# pip install pyinstaller-versionfile
# create-version-file metadata.yml --outfile version.txt
Version: 1.3.0
Version: 1.3.1
CompanyName: anidev
FileDescription: Бот для майнкрафта, использует RCON и VK API. Исходники можно найти по "SantaSpeen/Rcon-VK-Bot"
InternalName: VkBot-Rcon