mirror of
https://github.com/SantaSpeen/Rcon-VK-Bot.git
synced 2025-08-18 09:05:53 +00:00
Compare commits
4 Commits
4ed336cd73
...
a93f18fa47
Author | SHA1 | Date | |
---|---|---|---|
a93f18fa47 | |||
26c086ceb6 | |||
b20394ed6f | |||
28d9f62667 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -135,3 +135,4 @@ help_message.txt
|
|||||||
count*
|
count*
|
||||||
version.txt
|
version.txt
|
||||||
*.exe
|
*.exe
|
||||||
|
logs/
|
||||||
|
2
.idea/Rcon-VK-Bot.iml
generated
2
.idea/Rcon-VK-Bot.iml
generated
@ -6,7 +6,7 @@
|
|||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</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" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -3,5 +3,5 @@
|
|||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.10 (Rcon-VK-Bot)" />
|
<option name="sdkName" value="Python 3.10 (Rcon-VK-Bot)" />
|
||||||
</component>
|
</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>
|
</project>
|
@ -41,7 +41,7 @@ _Всё очень легко и просто)_
|
|||||||
|
|
||||||
## Система permissions
|
## Система permissions
|
||||||
|
|
||||||
В файле permissions.yam указаны все пользователи с "повышенным" уровнем доступа к боту
|
В файле `permissions.yml` указаны все пользователи с "повышенным" уровнем доступа к боту
|
||||||
Пример
|
Пример
|
||||||
```yaml
|
```yaml
|
||||||
noRole: Нет роли
|
noRole: Нет роли
|
||||||
@ -67,8 +67,6 @@ perms:
|
|||||||
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
|
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
|
||||||
useLuckPerms: false
|
useLuckPerms: false
|
||||||
LuckPerms:
|
LuckPerms:
|
||||||
# Смотрите настройку LuckPerms
|
|
||||||
server: global
|
|
||||||
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
|
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
|
||||||
storage-method: PostgreSQL
|
storage-method: PostgreSQL
|
||||||
data:
|
data:
|
||||||
@ -81,6 +79,7 @@ LuckPerms:
|
|||||||
password: user
|
password: user
|
||||||
|
|
||||||
# Смотрите настройку LuckPerms
|
# Смотрите настройку LuckPerms
|
||||||
|
server: global
|
||||||
table-prefix: luckperms_
|
table-prefix: luckperms_
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -3,3 +3,4 @@ mcrcon~=0.7.0
|
|||||||
vk~=3.0
|
vk~=3.0
|
||||||
ruamel.yaml~=0.18.5
|
ruamel.yaml~=0.18.5
|
||||||
requests~=2.31.0
|
requests~=2.31.0
|
||||||
|
loguru~=0.7.2
|
59
src/main.py
59
src/main.py
@ -3,21 +3,22 @@ import traceback
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
import vk
|
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:
|
class Bot:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.vk = vk.API(access_token=config.vk.token, v=5.199)
|
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:
|
with open('help_message.txt') as f:
|
||||||
self.help_message = f.read()
|
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):
|
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')
|
return lp.get('server'), lp.get('key'), lp.get('ts')
|
||||||
|
|
||||||
def write(self, peer_id, message):
|
def write(self, peer_id, message):
|
||||||
@ -25,7 +26,7 @@ class Bot:
|
|||||||
messages = (len(message) // 4095)
|
messages = (len(message) // 4095)
|
||||||
for i in range(1, messages + 1):
|
for i in range(1, messages + 1):
|
||||||
if i > 30:
|
if i > 30:
|
||||||
log("[BOT] Сообщение слишком длинное...", 1)
|
logger.info("[BOT] Сообщение слишком длинное...")
|
||||||
break
|
break
|
||||||
self.write(peer_id, message[:4095 * i])
|
self.write(peer_id, message[:4095 * i])
|
||||||
else:
|
else:
|
||||||
@ -37,13 +38,14 @@ class Bot:
|
|||||||
r = cmd
|
r = cmd
|
||||||
if a or _allow:
|
if a or _allow:
|
||||||
answer = rcon(cmd)
|
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:
|
if _write:
|
||||||
self.write(peer_id, f"RCON:\n{answer}")
|
self.write(peer_id, f"RCON:\n{answer}")
|
||||||
else:
|
else:
|
||||||
return answer
|
return answer
|
||||||
else:
|
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):
|
def message_handle(self, message):
|
||||||
from_id = message['from_id']
|
from_id = message['from_id']
|
||||||
@ -56,13 +58,13 @@ class Bot:
|
|||||||
self.write(peer_id, self.help_message)
|
self.write(peer_id, self.help_message)
|
||||||
case "!online":
|
case "!online":
|
||||||
online = get_server_status().online
|
online = get_server_status().online
|
||||||
self.write(peer_id, f"На сервере сейчас {online} {""}")
|
self.write(peer_id, f"На сервере сейчас {online}")
|
||||||
case "!id":
|
case "!id":
|
||||||
self.write(peer_id, f"Твой ID: {from_id}\nРоль: {perms.get_role(from_id)}")
|
self.write(peer_id, f"Твой ID: {from_id}\nРоль: {perms.get_role(from_id)}")
|
||||||
|
|
||||||
def listen(self):
|
def listen(self):
|
||||||
server, key, ts = self.get_lp_server()
|
server, key, ts = self.get_lp_server()
|
||||||
log("[BOT] Начинаю получать сообщения..")
|
logger.info("[BOT] Начинаю получать сообщения..")
|
||||||
while True:
|
while True:
|
||||||
lp = requests.get(f'{server}?act=a_check&key={key}&ts={ts}&wait=25').json()
|
lp = requests.get(f'{server}?act=a_check&key={key}&ts={ts}&wait=25').json()
|
||||||
try:
|
try:
|
||||||
@ -75,41 +77,36 @@ class Bot:
|
|||||||
|
|
||||||
ts = lp.get('ts')
|
ts = lp.get('ts')
|
||||||
|
|
||||||
except KeyboardInterrupt as e:
|
except KeyboardInterrupt:
|
||||||
raise e
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as i:
|
||||||
ts = lp.get('ts')
|
ts = lp.get('ts')
|
||||||
log(f"Found exception: {e}", 1)
|
logger.exception(i)
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if not config.vk.token:
|
if not config.vk.token:
|
||||||
log("Токен ВК не найден.\nВыход...", 1)
|
logger.error("Токен ВК не найден.")
|
||||||
input("\n\nНажмите Enter для продолжения..")
|
enter_to_exit()
|
||||||
sys.exit(1)
|
|
||||||
try:
|
try:
|
||||||
bot = Bot()
|
bot = Bot()
|
||||||
try:
|
|
||||||
# Test RCON
|
# Test RCON
|
||||||
bot.rcon_cmd_handle("list", 0, 0, False, True)
|
if rcon("list").startswith("Rcon error"):
|
||||||
log("RCON работает.")
|
logger.error("RCON не отвечает. Проверьте блок \"rcon\" в config.json")
|
||||||
except Exception as e:
|
enter_to_exit()
|
||||||
log("RCON не отвечает. Проверьте блок \"rcon\" с config.json", 1)
|
logger.info("RCON доступен.")
|
||||||
raise e
|
|
||||||
try:
|
try:
|
||||||
# Test Minecraft Server
|
# 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:
|
except Exception as e:
|
||||||
log("Сервер не отвечает. Проверьте блок \"minecraft\" с config.json", 1)
|
logger.exception(e)
|
||||||
raise e
|
logger.info("Сервер не отвечает. Проверьте блок \"minecraft\" в config.json")
|
||||||
|
enter_to_exit()
|
||||||
bot.listen()
|
bot.listen()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"Exception: {e}", 1)
|
logger.exception(e)
|
||||||
traceback.print_exc()
|
enter_to_exit()
|
||||||
finally:
|
|
||||||
log("Выход..")
|
|
||||||
input("\n\nНажмите Enter для продолжения..")
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import zipfile
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
from mcrcon import MCRcon
|
from mcrcon import MCRcon
|
||||||
|
|
||||||
from .perms import Permissions
|
from .perms import Permissions
|
||||||
|
|
||||||
|
|
||||||
def log(text, lvl=0):
|
|
||||||
print(f"[{datetime.now()}] [{['INFO ', 'ERROR'][lvl]}] {text}")
|
|
||||||
|
|
||||||
|
|
||||||
raw_config = """\
|
raw_config = """\
|
||||||
{
|
{
|
||||||
"vk": {
|
"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"):
|
if not os.path.exists("config.json"):
|
||||||
log("Создание: config.json...")
|
logger.info("Создание: config.json...")
|
||||||
with open("config.json", "w") as f:
|
with open("config.json", "w") as f:
|
||||||
f.write(raw_config)
|
f.write(raw_config)
|
||||||
|
|
||||||
with open('config.json') as f:
|
with open('config.json') as f:
|
||||||
config = json.load(f, object_hook=lambda x: namedtuple('X', x.keys())(*x.values()))
|
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):
|
if not os.path.exists(config.vk.help_file):
|
||||||
log(f"Создание: {config.vk.help_file}...")
|
logger.info(f"Создание: {config.vk.help_file}...")
|
||||||
with open(config.vk.help_file, "w") as f:
|
with open(config.vk.help_file, "w", encoding="utf-8") as f:
|
||||||
f.write(raw_help)
|
f.write(raw_help)
|
||||||
|
|
||||||
if config.minecraft.java:
|
if config.minecraft.java:
|
||||||
from mcstatus import JavaServer as mcs
|
from mcstatus import JavaServer as MineServer
|
||||||
else:
|
else:
|
||||||
from mcstatus import BedrockServer as mcs
|
from mcstatus import BedrockServer as MineServer
|
||||||
|
|
||||||
host = config.rcon.host
|
host = config.rcon.host
|
||||||
port = config.rcon.port
|
port = config.rcon.port
|
||||||
@ -71,15 +97,21 @@ def rcon(cmd):
|
|||||||
text = mcr.command(cmd)
|
text = mcr.command(cmd)
|
||||||
return re.sub(r'§.', '', text)
|
return re.sub(r'§.', '', text)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"[RCON] ERROR with command: {cmd}", 1)
|
logger.error(f"[RCON] ERROR with command: {cmd}")
|
||||||
print(traceback.format_exc())
|
logger.exception(e)
|
||||||
return f"Rcon error: {e}"
|
return f"Rcon error: {e}"
|
||||||
|
|
||||||
|
|
||||||
def get_server_status():
|
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()
|
return server.status()
|
||||||
|
|
||||||
|
|
||||||
Permissions.perm_file = Path(config.permissions_file)
|
Permissions.perm_file = Path(config.permissions_file)
|
||||||
perms = Permissions.load()
|
perms = Permissions.load()
|
||||||
|
|
||||||
|
|
||||||
|
def enter_to_exit():
|
||||||
|
logger.info("Выход..")
|
||||||
|
input("\nНажмите Enter для продолжения..")
|
||||||
|
sys.exit(1)
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import os.path
|
import os.path
|
||||||
from datetime import datetime
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
|
|
||||||
|
|
||||||
def log(text, lvl=0):
|
|
||||||
print(f"[{datetime.now()}] [{['INFO ', 'ERROR'][lvl]}] {text}")
|
|
||||||
|
|
||||||
|
|
||||||
yaml = YAML()
|
yaml = YAML()
|
||||||
yaml.default_flow_style = False
|
yaml.default_flow_style = False
|
||||||
|
|
||||||
@ -22,11 +18,11 @@ class Permissions:
|
|||||||
self._perms = kwargs['perms']
|
self._perms = kwargs['perms']
|
||||||
self._members = {}
|
self._members = {}
|
||||||
if kwargs['useLuckPerms']:
|
if kwargs['useLuckPerms']:
|
||||||
log("[PERMS] Using LuckPerms mode")
|
logger.info("[PERMS] Using LuckPerms mode")
|
||||||
log("[PERMS] LuckPerms mode support still in development")
|
logger.info("[PERMS] LuckPerms mode support still in development")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
self._luck_perms = kwargs['LuckPerms']
|
self._luck_perms = kwargs['LuckPerms']
|
||||||
log("[PERMS] Permissions loaded")
|
logger.info(f"[PERMS] {self.perm_file} - загружен")
|
||||||
self.__handle_members()
|
self.__handle_members()
|
||||||
|
|
||||||
def __handle_members(self):
|
def __handle_members(self):
|
||||||
@ -69,7 +65,7 @@ class Permissions:
|
|||||||
os.remove(cls.perm_file)
|
os.remove(cls.perm_file)
|
||||||
return Permissions.load()
|
return Permissions.load()
|
||||||
else:
|
else:
|
||||||
log(f"Generating permissions file: {cls.perm_file}")
|
logger.info(f"Generating permissions file: {cls.perm_file}")
|
||||||
import textwrap
|
import textwrap
|
||||||
raw = textwrap.dedent("""\
|
raw = textwrap.dedent("""\
|
||||||
noRole: Нет роли
|
noRole: Нет роли
|
||||||
@ -95,8 +91,7 @@ class Permissions:
|
|||||||
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
|
# Интеграция с базой данных LuckPerms (Нужна именно внешняя база данных)
|
||||||
useLuckPerms: false
|
useLuckPerms: false
|
||||||
LuckPerms:
|
LuckPerms:
|
||||||
# Смотрите настройку LuckPerms
|
|
||||||
server: global
|
|
||||||
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
|
# Разрешенные варианты: MySQL, MariaDB, PostgreSQL
|
||||||
storage-method: PostgreSQL
|
storage-method: PostgreSQL
|
||||||
data:
|
data:
|
||||||
@ -109,6 +104,7 @@ class Permissions:
|
|||||||
password: user
|
password: user
|
||||||
|
|
||||||
# Смотрите настройку LuckPerms
|
# Смотрите настройку LuckPerms
|
||||||
|
server: global
|
||||||
table-prefix: luckperms_
|
table-prefix: luckperms_
|
||||||
""")
|
""")
|
||||||
data = yaml.load(raw)
|
data = yaml.load(raw)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# pip install pyinstaller-versionfile
|
# pip install pyinstaller-versionfile
|
||||||
# create-version-file metadata.yml --outfile version.txt
|
# create-version-file metadata.yml --outfile version.txt
|
||||||
Version: 1.2.3
|
Version: 1.3.0
|
||||||
CompanyName: anidev
|
CompanyName: anidev
|
||||||
FileDescription: Бот для майнкрафта, использует RCON и VK API. Исходники можно найти по "SantaSpeen/Rcon-VK-Bot"
|
FileDescription: Бот для майнкрафта, использует RCON и VK API. Исходники можно найти по "SantaSpeen/Rcon-VK-Bot"
|
||||||
InternalName: VkBot-Rcon
|
InternalName: VkBot-Rcon
|
||||||
|
Loading…
x
Reference in New Issue
Block a user