Compare commits

...

4 Commits

Author SHA1 Message Date
a93f18fa47 [~] encoding 2024-01-12 21:10:25 +03:00
26c086ceb6 .idea 2024-01-12 21:09:00 +03:00
b20394ed6f [~] 1.2.3 -> 1.3.0
[!] Critical bugfix
[+] Logging
[+] Logging to file
[~] Minor
2024-01-12 21:08:46 +03:00
28d9f62667 .idea 2024-01-12 21:04:49 +03:00
9 changed files with 91 additions and 65 deletions

1
.gitignore vendored
View File

@ -135,3 +135,4 @@ help_message.txt
count*
version.txt
*.exe
logs/

2
.idea/Rcon-VK-Bot.iml generated
View File

@ -6,7 +6,7 @@
<excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (Rcon-VK-Bot) (2)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.12 (Rcon-VK-Bot)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.idea/misc.xml generated
View File

@ -3,5 +3,5 @@
<component name="Black">
<option name="sdkName" value="Python 3.10 (Rcon-VK-Bot)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Rcon-VK-Bot) (2)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Rcon-VK-Bot)" project-jdk-type="Python SDK" />
</project>

View File

@ -41,7 +41,7 @@ _Всё очень легко и просто)_
## Система permissions
В файле permissions.yam указаны все пользователи с "повышенным" уровнем доступа к боту
В файле `permissions.yml` указаны все пользователи с "повышенным" уровнем доступа к боту
Пример
```yaml
noRole: Нет роли
@ -67,8 +67,6 @@ perms:
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
useLuckPerms: false
LuckPerms:
# Смотрите настройку LuckPerms
server: global
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
storage-method: PostgreSQL
data:
@ -81,6 +79,7 @@ LuckPerms:
password: user
# Смотрите настройку LuckPerms
server: global
table-prefix: luckperms_
```

View File

@ -3,3 +3,4 @@ mcrcon~=0.7.0
vk~=3.0
ruamel.yaml~=0.18.5
requests~=2.31.0
loguru~=0.7.2

View File

@ -3,21 +3,22 @@ import traceback
import requests
import vk
from loguru import logger
from modules import log, config, rcon, perms, get_server_status
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 = vk.groups.getById()[0]['id']
self.group_id = self.vk.groups.getById()['groups'][0]['id']
with open('help_message.txt') as f:
self.help_message = f.read()
log(f"[BOT] ID группы: {self.group_id}")
logger.info(f"[BOT] ID группы: {self.group_id}")
def get_lp_server(self):
lp = vk.groups.getLongPollServer(group_id=self.group_id)
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):
@ -25,7 +26,7 @@ class Bot:
messages = (len(message) // 4095)
for i in range(1, messages + 1):
if i > 30:
log("[BOT] Сообщение слишком длинное...", 1)
logger.info("[BOT] Сообщение слишком длинное...")
break
self.write(peer_id, message[:4095 * i])
else:
@ -37,13 +38,14 @@ class Bot:
r = cmd
if a or _allow:
answer = rcon(cmd)
log(f"[BOT] User: {from_id}({r}) in Chat: {peer_id} use RCON cmd: \"{cmd}\", with answer: \"{answer}\"")
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:
log(f"[BOT] User: {from_id}({r}) in Chat: {peer_id} no have rights RCON cmd: \"{cmd}\".")
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']
@ -56,13 +58,13 @@ class Bot:
self.write(peer_id, self.help_message)
case "!online":
online = get_server_status().online
self.write(peer_id, f"На сервере сейчас {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()
log("[BOT] Начинаю получать сообщения..")
logger.info("[BOT] Начинаю получать сообщения..")
while True:
lp = requests.get(f'{server}?act=a_check&key={key}&ts={ts}&wait=25').json()
try:
@ -75,41 +77,36 @@ class Bot:
ts = lp.get('ts')
except KeyboardInterrupt as e:
raise e
except KeyboardInterrupt:
raise KeyboardInterrupt
except Exception as e:
except Exception as i:
ts = lp.get('ts')
log(f"Found exception: {e}", 1)
traceback.print_exc()
logger.exception(i)
if __name__ == '__main__':
if not config.vk.token:
log("Токен ВК не найден.\nВыход...", 1)
input("\n\nНажмите Enter для продолжения..")
sys.exit(1)
logger.error("Токен ВК не найден.")
enter_to_exit()
try:
bot = Bot()
try:
# Test RCON
bot.rcon_cmd_handle("list", 0, 0, False, True)
log("RCON работает.")
except Exception as e:
log("RCON не отвечает. Проверьте блок \"rcon\" с config.json", 1)
raise e
if rcon("list").startswith("Rcon error"):
logger.error("RCON не отвечает. Проверьте блок \"rcon\" в config.json")
enter_to_exit()
logger.info("RCON доступен.")
try:
# Test Minecraft Server
log(f"Проверка сервера. Онлайн: {get_server_status().online}")
players = get_server_status().players
logger.info(f"Проверка сервера. Онлайн: {players.online}/{players.max}")
except Exception as e:
log("Сервер не отвечает. Проверьте блок \"minecraft\" с config.json", 1)
raise e
logger.exception(e)
logger.info("Сервер не отвечает. Проверьте блок \"minecraft\" в config.json")
enter_to_exit()
bot.listen()
except KeyboardInterrupt:
pass
except Exception as e:
log(f"Exception: {e}", 1)
traceback.print_exc()
finally:
log("Выход..")
input("\n\nНажмите Enter для продолжения..")
logger.exception(e)
enter_to_exit()

View File

@ -1,20 +1,19 @@
import glob
import json
import os
import re
import sys
import traceback
import zipfile
from collections import namedtuple
from datetime import datetime
from pathlib import Path
from loguru import logger
from mcrcon import MCRcon
from .perms import Permissions
def log(text, lvl=0):
print(f"[{datetime.now()}] [{['INFO ', 'ERROR'][lvl]}] {text}")
raw_config = """\
{
"vk": {
@ -41,24 +40,51 @@ raw_help = """\
Бот сделан кожанным петухом - админом, все вопросы к нему, я не причём.
"""
def zip_logs():
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_file)
logger.add(sys.stdout, format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | {message}")
zip_logs()
if not os.path.exists("config.json"):
log("Создание: config.json...")
logger.info("Создание: config.json...")
with open("config.json", "w") as f:
f.write(raw_config)
with open('config.json') as f:
config = json.load(f, object_hook=lambda x: namedtuple('X', x.keys())(*x.values()))
log("Запуск..")
logger.info("Запуск..")
if not os.path.exists(config.vk.help_file):
log(f"Создание: {config.vk.help_file}...")
with open(config.vk.help_file, "w") as f:
logger.info(f"Создание: {config.vk.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 mcs
from mcstatus import JavaServer as MineServer
else:
from mcstatus import BedrockServer as mcs
from mcstatus import BedrockServer as MineServer
host = config.rcon.host
port = config.rcon.port
@ -71,15 +97,21 @@ def rcon(cmd):
text = mcr.command(cmd)
return re.sub(r'§.', '', text)
except Exception as e:
log(f"[RCON] ERROR with command: {cmd}", 1)
print(traceback.format_exc())
logger.error(f"[RCON] ERROR with command: {cmd}")
logger.exception(e)
return f"Rcon error: {e}"
def get_server_status():
server = mcs.lookup(config.minecraft.host, config.minecraft.port)
server = MineServer.lookup(config.minecraft.host, config.minecraft.port)
return server.status()
Permissions.perm_file = Path(config.permissions_file)
perms = Permissions.load()
def enter_to_exit():
logger.info("Выход..")
input("\nНажмите Enter для продолжения..")
sys.exit(1)

View File

@ -1,14 +1,10 @@
import os.path
from datetime import datetime
import sys
from pathlib import Path
from loguru import logger
from ruamel.yaml import YAML
def log(text, lvl=0):
print(f"[{datetime.now()}] [{['INFO ', 'ERROR'][lvl]}] {text}")
yaml = YAML()
yaml.default_flow_style = False
@ -22,11 +18,11 @@ class Permissions:
self._perms = kwargs['perms']
self._members = {}
if kwargs['useLuckPerms']:
log("[PERMS] Using LuckPerms mode")
log("[PERMS] LuckPerms mode support still in development")
logger.info("[PERMS] Using LuckPerms mode")
logger.info("[PERMS] LuckPerms mode support still in development")
sys.exit(1)
self._luck_perms = kwargs['LuckPerms']
log("[PERMS] Permissions loaded")
logger.info(f"[PERMS] {self.perm_file} - загружен")
self.__handle_members()
def __handle_members(self):
@ -69,7 +65,7 @@ class Permissions:
os.remove(cls.perm_file)
return Permissions.load()
else:
log(f"Generating permissions file: {cls.perm_file}")
logger.info(f"Generating permissions file: {cls.perm_file}")
import textwrap
raw = textwrap.dedent("""\
noRole: Нет роли
@ -95,8 +91,7 @@ class Permissions:
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
useLuckPerms: false
LuckPerms:
# Смотрите настройку LuckPerms
server: global
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
storage-method: PostgreSQL
data:
@ -109,6 +104,7 @@ class Permissions:
password: user
# Смотрите настройку LuckPerms
server: global
table-prefix: luckperms_
""")
data = yaml.load(raw)

View File

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