20 Commits
v1.5 ... v1.8

Author SHA1 Message Date
e56268543c Migrated to cogs 2023-01-17 15:50:47 +01:00
be9ed44614 Updated readme 2023-01-17 15:26:43 +01:00
eec975ce36 Added notice about linux dependencies 2023-01-17 15:24:34 +01:00
512517f76e Fixed imports 2023-01-17 15:21:26 +01:00
af29a7ced6 Added one more optional dependency 2023-01-17 15:14:10 +01:00
9b8d389031 Updated README 2023-01-17 15:10:04 +01:00
0f7a860dad Updated requirements 2023-01-17 15:06:51 +01:00
a3a0aaa72b Tasty refactor 2023-01-17 15:05:39 +01:00
78239c7e5d Update 1.7 2022-08-03 11:22:06 +03:00
79fb14b1bc Update 1.7 2022-08-03 10:19:17 +02:00
0379d99e69 Update 1.7 2022-08-03 10:11:40 +02:00
462f4c7a09 Update 1.7 2022-08-03 10:11:04 +02:00
078eac1446 Updated version check path 2022-07-22 10:54:57 +03:00
6334002068 Updated repo badges 2022-07-20 22:29:39 +03:00
ffa3babb45 Some minor locale changes 2022-07-20 14:14:58 +03:00
fba819ce29 Update 'locale/uk.json' 2022-07-20 14:14:04 +03:00
7ccb674b9f Update 'locale/uk.json' 2022-07-20 14:06:53 +03:00
b6e9f5089c Changed some small things 2022-07-20 14:06:36 +03:00
85480c7658 Added "localized" badge 2022-07-20 00:25:10 +03:00
Profitroll
78a845a99b Update 1.6 2022-07-15 22:28:01 +02:00
11 changed files with 382 additions and 346 deletions

View File

@@ -1,34 +1,37 @@
# YusarinBot # YusarinBot
[![License: GPL v2](https://img.shields.io/badge/License-GPL_v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) [![Crowdin](https://badges.crowdin.net/yusarinbot/localized.svg)](https://crowdin.com/project/yusarinbot) ![Discord](https://img.shields.io/discord/940760922346123284)
Simple, easy to set up, yet reliable. Simple, easy to set up, yet reliable.
A specially designed bot for creating private voice channels on your Discord servers. A specially designed bot for creating private voice channels on your Discord servers.
Installation instructions are listed below. Please, make sure you have installed dependencies before opening issue here. Installation instructions are listed below. Please, make sure you have installed dependencies before opening issue here.
> Since version 1.5 only slash commands are supported. If you'd like to use old-fashioned commands and commands prefix feature consider installing [1.4 version](https://github.com/profitrollgame/YusarinBot/releases/tag/v1.4) which is the last one compatible with those. > Since version 1.5 only slash commands are supported. If you'd like to use old-fashioned commands and commands prefix feature consider installing [1.4 version](https://git.end-play.xyz/profitroll/YusarinBot/releases/tag/v1.4) which is the last one compatible with those.
## Installation (Short) ## Installation (Short)
1. `git clone https://github.com/profitrollgame/YusarinBot` 1. `git clone https://git.end-play.xyz/profitroll/YusarinBot`
2. `cd YusarinBot` 2. `cd YusarinBot`
3. `pip install -r requirements.txt` 3. `pip install -r requirements.txt`
4. `nano config.json` 4. `nano config.json`
5. `python yusarin.py` 5. `python yusarin.py`
## Installation (Detailed) ## Installation (Detailed)
1. Download and install Python 3 (3.6+ should be fine) 1. Download and install Python 3.8+
2. Download bot's release: https://github.com/profitrollgame/YusarinBot/releases/latest 2. Download bot's release: https://git.end-play.xyz/profitroll/YusarinBot/releases/latest
3. Unpack your archive to folder. Name it YusarinBot for example 3. Unpack your archive to folder. Name it YusarinBot for example
4. Change working directory using `cd YusarinBot` or `cd FolderYouHaveCreated` 4. Change working directory using `cd YusarinBot` or `cd FolderYouHaveCreated`
5. Run `pip install -r requirements.txt` to install dependencies 5. If you're using Linux system - install [dependencies for Pycord](https://docs.pycord.dev/en/stable/installing.html#installing)
6. Create new Discord application at https://discord.com/developers/applications 6. Run `pip install -r requirements.txt` to install dependencies
7. You can rename it however you want, set needed descriptions etc 7. [Optionally] run `pip install -r requirements-optional.txt` to install optional dependencies
8. Go to "Bot" tab and enable application as bot 8. Create new Discord application at https://discord.com/developers/applications
9. Copy token of your bot 9. You can rename it however you want, set needed descriptions etc
10. Open file `config.json` with your favorite text editor and paste your token as value of "bot_token" key 10. Go to "Bot" tab and enable application as bot
11. Copy your own Discord ID and paste it as value of "owner" key (How to get ID: https://support.playhive.com/discord-user-id/) 11. Copy token of your bot
12. Bot is ready! Run it using `python yusarin.py` 12. Open file `config.json` with your favorite text editor and paste your token as value of "bot_token" key
13. Copy your own Discord ID and paste it as value of "owner" key (How to get ID: https://support.playhive.com/discord-user-id/)
14. Bot is ready! Run it using `python yusarin.py`
## Config explanation ## Config explanation
Default configuration file is [available here](https://github.com/profitrollgame/YusarinBot/blob/main/config.json) Default configuration file is [available here](https://git.end-play.xyz/profitroll/YusarinBot/blob/main/config.json)
- "debug" - Option that enables more detailed log messages [Boolean] - "debug" - Option that enables more detailed log messages [Boolean]
- "owner" - Discord ID of user who will be able to execute admin commands (`$shutdown` for example) [Integer] - "owner" - Discord ID of user who will be able to execute admin commands (`$shutdown` for example) [Integer]
- "bot_name" - Name of your bot. Is not used anywhere yet [String] - "bot_name" - Name of your bot. Is not used anywhere yet [String]
@@ -40,6 +43,7 @@ Default configuration file is [available here](https://github.com/profitrollgame
- "color_ok" - HTML color used on command success embed [String] - "color_ok" - HTML color used on command success embed [String]
- "color_warn" - HTML color used in all warning embeds [String] - "color_warn" - HTML color used in all warning embeds [String]
- "color_error" - HTML color used on command error embed [String] - "color_error" - HTML color used on command error embed [String]
- "enable_nomic" - Option that allows you to create additional text channel for "nomic" purposes [Boolean]
- "bot_site" - http or https link that will be used in help message header [String] - "bot_site" - http or https link that will be used in help message header [String]
- "bot_icon" - http or https link that will be used in help message header's icon [String] - "bot_icon" - http or https link that will be used in help message header's icon [String]
- "check_for_updates" - Option that defines whether bot should check for a new version available [Boolean] - "check_for_updates" - Option that defines whether bot should check for a new version available [Boolean]
@@ -49,3 +53,4 @@ Default configuration file is [available here](https://github.com/profitrollgame
## Extra ## Extra
1. Bot doesn't have any self updaters **yet** 1. Bot doesn't have any self updaters **yet**
2. You can add public version of the bot to your Discord server using this link: https://www.end-play.xyz/yusarin/invite 2. You can add public version of the bot to your Discord server using this link: https://www.end-play.xyz/yusarin/invite
3. If you have questions/suggestions - just join our [Discord Support Server](https://discord.com/invite/2zpzErtnNT)

35
cogs/cogCategory.py Normal file
View File

@@ -0,0 +1,35 @@
from discord import ApplicationContext, Option, SlashCommandGroup, CategoryChannel
from discord.ext import commands
from functions import getMsg, guildConfGet, guildConfReset, guildConfSet, loadJson, makeEmbed, strToColor
#=========================================================================================================================
class CogCategory(commands.Cog):
def __init__(self, client):
self.client = client
category = SlashCommandGroup("category", "Commands related to parent channels category")
@category.command(name="set", description="Select the voice channel that will be parent to private ones")
async def category_set(self, ctx: ApplicationContext, category: Option(CategoryChannel, "Parent Channel Category")): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
guildConfSet(ctx.guild, "category", category.id)
await ctx.respond(embed=makeEmbed(title=getMsg("set_category_title", ctx.guild), description=getMsg("set_category_description", ctx.guild).format(category.name), color=strToColor(config["color_ok"])))
if guildConfGet(ctx.guild, "channel") is None:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_channel_title", ctx.guild), description=getMsg("hint_none_channel_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
@category.command(name="reset", description="Reset the currently selected parent channel category")
async def category_reset(self, ctx: ApplicationContext): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if guildConfGet(ctx.guild, "category") is not None:
guildConfReset(ctx.guild, "category")
await ctx.respond(embed=makeEmbed(title=getMsg("reset_category_title", ctx.guild), description=getMsg("reset_category_description", ctx.guild), color=strToColor(config["color_ok"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_category_title", ctx.guild), description=getMsg("hint_none_category_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================

75
cogs/cogChannel.py Normal file
View File

@@ -0,0 +1,75 @@
from discord import ApplicationContext, SlashCommandGroup, VoiceChannel, Member, VoiceState, Cog, option, utils
from discord.ext import commands
from functions import appendLog, changeNomicPerms, createUserVoice, getMsg, guildConfGet, guildConfReset, guildConfSet, isUserVoice, isVoiceOfUser, loadJson, makeEmbed, removeUserVoice, strToColor
#=========================================================================================================================
class CogChannel(commands.Cog):
def __init__(self, client):
self.client = client
@Cog.listener()
async def on_voice_state_update(self, member: Member, before: VoiceState, after: VoiceState):
config = loadJson("config.json")
vc_from = before.channel
vc_to = after.channel
# If user left vc
if vc_to is None:
if isUserVoice(vc_from):
if isVoiceOfUser(vc_from, member):
await removeUserVoice(vc_from)
return
else:
if loadJson("config.json")["enable_nomic"]:
await changeNomicPerms("deny", vc_from, member)
# If user joined vc
else:
if isUserVoice(vc_from):
if isVoiceOfUser(vc_from, member):
await removeUserVoice(vc_from)
else:
if loadJson("config.json")["enable_nomic"]:
await changeNomicPerms("deny", vc_from, member)
if isUserVoice(vc_to):
if loadJson("config.json")["enable_nomic"]:
await changeNomicPerms("allow", vc_to, member)
if vc_to.id == guildConfGet(vc_to.guild, "channel"):
if guildConfGet(vc_to.guild, "category") is not None:
voice_chan = await createUserVoice(vc_to, utils.get(vc_to.guild.categories, id=guildConfGet(vc_to.guild, "category")), member)
try:
await member.move_to(voice_chan)
except:
await removeUserVoice(voice_chan)
else:
appendLog(f"Category for guild {vc_to.guild} ({str(vc_to.guild.id)}) is not set", guild=vc_to.guild)
channel = SlashCommandGroup("channel", "Commands related to parent voice channel")
@channel.command(name="set", description="Select the voice channel that will be parent to private ones")
@option("channel", description="Parent Voice Channel")
async def channel_set(self, ctx: ApplicationContext, channel: VoiceChannel):
config = loadJson("config.json")
if ctx.guild is not None:
guildConfSet(ctx.guild, "channel", channel.id)
await ctx.respond(embed=makeEmbed(title=getMsg("set_channel_title", ctx.guild), description=getMsg("set_channel_description", ctx.guild).format(channel.name), color=strToColor(config["color_ok"])))
if guildConfGet(ctx.guild, "category") is None:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_category_title", ctx.guild), description=getMsg("hint_none_category_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
@channel.command(name="reset", description="Reset the currently selected parent voice channel")
async def channel_reset(self, ctx: ApplicationContext):
config = loadJson("config.json")
if ctx.guild is not None:
if guildConfGet(ctx.guild, "channel") is not None:
guildConfReset(ctx.guild, "channel")
await ctx.respond(embed=makeEmbed(title=getMsg("reset_channel_title", ctx.guild), description=getMsg("reset_channel_description", ctx.guild), color=strToColor(config["color_ok"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_channel_title", ctx.guild), description=getMsg("hint_none_channel_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================

48
cogs/cogLocale.py Normal file
View File

@@ -0,0 +1,48 @@
from os import listdir
from discord import ApplicationContext, Option, SlashCommandGroup
from discord.ext import commands
from functions import appendLog, getMsg, guildConfGet, guildConfReset, guildConfSet, loadJson, makeEmbed, strToColor
#=========================================================================================================================
class CogLocale(commands.Cog):
def __init__(self, client):
self.client = client
locale = SlashCommandGroup("locale", "Commands related to bot's locale")
valid_locales = []
files_locales = listdir(f"locale/")
for entry in files_locales:
valid_locales.append(".".join(entry.split(".")[:-1]))
@locale.command(name="set", description="Set bot's messages language")
async def locale_set(self, ctx: ApplicationContext, language: Option(str, "One of the languages in list", choices=valid_locales)): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if language+".json" in listdir(f"locale/"):
guildConfSet(ctx.guild, "locale", language)
appendLog(f"Server's locale is now set to {language}", ctx.guild)
await ctx.respond(embed=makeEmbed(title=getMsg("set_locale_title", ctx.guild), description=getMsg("set_locale_description", ctx.guild).format(getMsg("locale_name", ctx.guild)), color=strToColor(config["color_ok"])))
else:
valid_locales = []
files_locales = listdir(f"locale/")
for entry in files_locales:
valid_locales.append(entry.split(".")[:-1])
await ctx.respond(embed=makeEmbed(title=getMsg("error_locale_title", ctx.guild), description=getMsg("error_locale_description", ctx.guild).format(", ".join(valid_locales)), color=strToColor(config["color_error"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
@locale.command(name="reset", description="Reset the bot's language in this guild")
async def locale_reset(self, ctx: ApplicationContext): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if guildConfGet(ctx.guild, "locale") is not None:
guildConfReset(ctx.guild, "locale")
appendLog(f"Server's locale has been reset", ctx.guild)
await ctx.respond(embed=makeEmbed(title=getMsg("reset_locale_title", ctx.guild), description=getMsg("reset_locale_description", ctx.guild).format(getMsg("locale_name", ctx.guild)), color=strToColor(config["color_ok"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_locale_title", ctx.guild), description=getMsg("hint_none_locale_description", ctx.guild).format(getMsg("locale_name", ctx.guild)), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================

40
cogs/cogUtility.py Normal file
View File

@@ -0,0 +1,40 @@
from os import getpid, makedirs, system
from shutil import rmtree
from discord import ApplicationContext, slash_command, Cog, Guild
from discord.ext import commands
from functions import appendLog, getMsg, loadJson, makeEmbed, saveJson, strToColor
#=========================================================================================================================
class CogUtility(commands.Cog):
def __init__(self, client):
self.client = client
@Cog.listener()
async def on_guild_join(self, guild: Guild):
makedirs(f"guilds/{str(guild.id)}", exist_ok=True)
makedirs(f"guilds/{str(guild.id)}/channels", exist_ok=True)
saveJson({}, f"guilds/{str(guild.id)}/config.json")
appendLog(f"Joined guild '{guild}' with id {str(guild.id)}")
@Cog.listener()
async def on_guild_remove(self, guild: Guild):
try:
rmtree(f"guilds/{str(guild.id)}")
except:
pass
appendLog(f"Left guild '{guild}' with id {str(guild.id)}")
@slash_command(name="shutdown", description="Restart the bot")
async def shutdown(self, ctx: ApplicationContext):
config = loadJson("config.json")
if ctx.author.id == config["owner"]:
await ctx.respond(embed=makeEmbed(description=getMsg("shutdown", ctx.guild).format(ctx.author), color=strToColor(config["color_default"])))
system(f"kill -9 {str(getpid())}")
else:
await ctx.respond(embed=makeEmbed(title=getMsg("admin_title", ctx.guild), description=getMsg("admin_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================

View File

@@ -10,6 +10,7 @@
"color_ok": "#226699", "color_ok": "#226699",
"color_warn": "#FFCC4D", "color_warn": "#FFCC4D",
"color_error": "#DD2E44", "color_error": "#DD2E44",
"enable_nomic": false,
"bot_site": "https://www.end-play.xyz/yusarin", "bot_site": "https://www.end-play.xyz/yusarin",
"bot_icon": "https://www.end-play.xyz/yusarin/logo", "bot_icon": "https://www.end-play.xyz/yusarin/logo",
"check_for_updates": true, "check_for_updates": true,

View File

@@ -1,14 +1,16 @@
import gzip from gzip import open as gzipopen
import os
import sys
import json
import shutil
import discord # type: ignore
from datetime import datetime from datetime import datetime
from pathlib import Path from os import listdir, makedirs, remove, stat, path
from shutil import copyfileobj
from typing import Any, Literal, Union
from discord import utils, Embed, TextChannel, VoiceChannel, PermissionOverwrite, Guild, CategoryChannel, Member, Client, ApplicationContext, Message
path = Path(__file__).resolve().parent try:
from ujson import loads, dumps
except ImportError:
from json import loads, dumps
# path = Path(__file__).resolve().parent
log_size = 512 log_size = 512
@@ -19,7 +21,7 @@ debug = False
try: try:
with open("config.json", 'r', encoding="utf-8") as json_file: with open("config.json", 'r', encoding="utf-8") as json_file:
output = json.load(json_file) output = loads(json_file.read())
json_file.close() json_file.close()
debug = output["debug"] debug = output["debug"]
except: except:
@@ -27,30 +29,28 @@ except:
# Check latest log size # Check latest log size
def checkSize(): def checkSize():
global path
path = str(path)
try: try:
if not os.path.isdir(f"{path}/logs"): if not path.isdir("logs"):
os.mkdir(f"{path}/logs") makedirs("logs", exist_ok=True)
log = os.stat(path + '/logs/latest.log') log = stat("logs/latest.log")
global log_size global log_size
if (log.st_size / 1024) > log_size: if (log.st_size / 1024) > log_size:
with open(path + '/logs/latest.log', 'rb') as f_in: with open("logs/latest.log", "rb") as f_in:
with gzip.open(f'{path}/logs/{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz', 'wb') as f_out: with gzipopen(f'logs/{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz', 'wb') as f_out:
shutil.copyfileobj(f_in, f_out) copyfileobj(f_in, f_out)
print(f'Copied {path}/logs/{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz') print(f'Copied logs/{datetime.now().strftime("%d.%m.%Y_%H:%M:%S")}.log.gz')
open(path + '/logs/latest.log', 'w').close() open("logs/latest.log", "w").close()
except FileNotFoundError: except FileNotFoundError:
print('Not found') print('Not found')
pass pass
# Append string to log # Append string to log
def appendLog(message, guild=None, announce=True): def appendLog(message: str, guild: Union[Guild, None] = None, announce=True) -> None:
global debug global debug
global path
if guild == None: if guild == None:
message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] {message}' message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] {message}'
@@ -61,24 +61,24 @@ def appendLog(message, guild=None, announce=True):
message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] [{guild}] {message}' message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] [{guild}] {message}'
if announce: if announce:
print(message_formatted) print(message_formatted, flush=True)
checkSize() checkSize()
log = open(path + '/logs/latest.log', 'a') # type: ignore log = open('logs/latest.log', 'a') # type: ignore
log.write(f'{message_formatted}\n') log.write(f'{message_formatted}\n')
log.close() log.close()
def saveJson(value, filename): def saveJson(value: Any, filename: str) -> None:
with open(filename, 'w', encoding="utf-8") as f: with open(filename, 'w', encoding="utf-8") as f:
json.dump(value, f, indent=4, ensure_ascii=False) f.write(dumps(value, indent=4, ensure_ascii=False))
f.close() f.close()
def loadJson(filename): def loadJson(filename: str) -> Any:
global debug global debug
try: try:
with open(filename, 'r', encoding="utf-8") as json_file: with open(filename, 'r', encoding="utf-8") as json_file:
output = json.load(json_file) output = loads(json_file.read())
json_file.close() json_file.close()
except Exception as exp: except Exception as exp:
if debug: if debug:
@@ -89,57 +89,53 @@ def loadJson(filename):
def colorToStr(): def colorToStr():
pass pass
def strToColor(string): def strToColor(string: str) -> int:
return int(hex(int(string.replace("#", ""), 16)), 0) return int(hex(int(string.replace("#", ""), 16)), 0)
def gotCommand(message): def gotCommand(message: Message) -> None:
global debug global debug
if debug: if debug:
appendLog(f"Command '{message.content}' from {message.author} ({str(message.author.id)})", message.guild) appendLog(f"Command '{message.content}' from {message.author} ({str(message.author.id)})", message.guild)
else: else:
appendLog(f"Command '{message.content}' from {message.author}", message.guild) appendLog(f"Command '{message.content}' from {message.author}", message.guild)
def guildConfGet(guild, variable): def guildConfGet(guild: Guild, variable: str) -> Any:
global path
global debug global debug
try: try:
config = loadJson(f"{path}/guilds/{str(guild.id)}/config.json") config = loadJson(f"guilds/{str(guild.id)}/config.json")
return config[variable] return config[variable]
except Exception as exp: except Exception as exp:
if debug: if debug:
appendLog(f"Could not get guild config key '{variable}' due to {exp}", guild) appendLog(f"Could not get guild config key '{variable}' due to {exp}", guild)
return None return None
def guildConfSet(guild, variable, value): def guildConfSet(guild: Guild, variable: str, value: Any) -> None:
global path config = loadJson(f"guilds/{str(guild.id)}/config.json")
config = loadJson(f"{path}/guilds/{str(guild.id)}/config.json")
config[variable] = value config[variable] = value
try: try:
saveJson(config, f"{path}/guilds/{str(guild.id)}/config.json") saveJson(config, f"guilds/{str(guild.id)}/config.json")
except: except:
os.mkdir(f"{path}/guilds/{str(guild.id)}") makedirs(f"guilds/{str(guild.id)}", exist_ok=True)
os.mkdir(f"{path}/guilds/{str(guild.id)}/channels") makedirs(f"guilds/{str(guild.id)}/channels", exist_ok=True)
saveJson(config, f"{path}/guilds/{str(guild.id)}/config.json") saveJson(config, f"guilds/{str(guild.id)}/config.json")
appendLog(f"Guild config key '{variable}' is now set to '{value}'", guild) appendLog(f"Guild config key '{variable}' is now set to '{value}'", guild)
def guildConfReset(guild, variable): def guildConfReset(guild: Guild, variable: str) -> None:
global path
try: try:
config = loadJson(f"{path}/guilds/{str(guild.id)}/config.json") config = loadJson(f"guilds/{str(guild.id)}/config.json")
del config[variable] del config[variable]
try: try:
saveJson(config, f"{path}/guilds/{str(guild.id)}/config.json") saveJson(config, f"guilds/{str(guild.id)}/config.json")
except: except:
os.mkdir(f"{path}/guilds/{str(guild.id)}") makedirs(f"guilds/{str(guild.id)}", exist_ok=True)
os.mkdir(f"{path}/guilds/{str(guild.id)}/channels") makedirs(f"guilds/{str(guild.id)}/channels", exist_ok=True)
saveJson(config, f"{path}/guilds/{str(guild.id)}/config.json") saveJson(config, f"guilds/{str(guild.id)}/config.json")
appendLog(f"Guild config key '{variable}' has been reset", guild) appendLog(f"Guild config key '{variable}' has been reset", guild)
except Exception as exp: except Exception as exp:
appendLog(f"Could not reset guild config key '{variable}' due to {exp}", guild) appendLog(f"Could not reset guild config key '{variable}' due to {exp}", guild)
def guildLocaleGet(guild): def guildLocaleGet(guild: Guild) -> str:
global path config = loadJson(f"config.json")
config = loadJson(f"{path}/config.json")
try: try:
locale = guildConfGet(guild, "locale") locale = guildConfGet(guild, "locale")
except: except:
@@ -149,57 +145,54 @@ def guildLocaleGet(guild):
else: else:
return locale return locale
def getMsg(string, guild=None): def getMsg(string: str, guild: Union[Guild, None] = None) -> str:
global path
config = loadJson("config.json")
try: try:
locale = loadJson(f'{path}/locale/{guildLocaleGet(guild)}.json') locale = loadJson(f'locale/{guildLocaleGet(guild)}.json')
return locale["messages"][string] return locale["messages"][string]
except Exception as exp: except Exception as exp:
appendLog(f"Could not get locale string named {string} due to exception {exp}", guild) appendLog(f"Could not get locale string named {string} due to exception {exp}", guild)
return string return string
def makeEmbed(title="", description="", footer="", color=0xffffff): def makeEmbed(title="", description="", footer="", color=0xffffff) -> Embed:
embed=discord.Embed(title=title, description=description, color=color) embed=Embed(title=title, description=description, color=color)
if footer is not None: if footer is not None:
embed.set_footer(text=footer) embed.set_footer(text=footer)
return embed return embed
def channelExists(number, guild, type="Any"): def channelExists(number: int, guild: Guild, type: Literal["Any", "Text", "Voice"] = "Any") -> bool:
global debug global debug
if number == None: if number == None:
return False return False
try: try:
if type == "Voice": if type == "Voice":
selected_channel = discord.utils.get(guild.channels, id=number) selected_channel = utils.get(guild.channels, id=number)
if isinstance(selected_channel, discord.VoiceChannel): if isinstance(selected_channel, VoiceChannel):
return True return True
elif type == "Text": elif type == "Text":
selected_channel = discord.utils.get(guild.channels, id=number) selected_channel = utils.get(guild.channels, id=number)
if isinstance(selected_channel, discord.TextChannel): if isinstance(selected_channel, TextChannel):
return True return True
elif type == "Any": elif type == "Any":
selected_channel = discord.utils.get(guild.channels, id=number) selected_channel = utils.get(guild.channels, id=number)
return True return True
except Exception as exp: except Exception as exp:
if debug: if debug:
appendLog(f"Channel ID {str(number)} is not a channel due to {exp}") appendLog(f"Channel ID {str(number)} is not a channel due to {exp}")
return False return False
def channelGetName(number, guild): def channelGetName(number: int, guild: Guild):
global debug global debug
try: try:
selected_channel = discord.utils.get(guild.channels, id=number) selected_channel = utils.get(guild.channels, id=number)
return selected_channel.name return selected_channel.name
except Exception as exp: except Exception as exp:
if debug: if debug:
appendLog(f"Channel ID {str(number)} is not a channel due to {exp}") appendLog(f"Channel ID {str(number)} is not a channel due to {exp}")
return "Channel doesn't exist" return "Channel doesn't exist"
def isUserVoice(vc): def isUserVoice(vc: VoiceChannel) -> bool:
global path
try: try:
channels_list = os.listdir(f"{path}/guilds/{str(vc.guild.id)}/channels/") channels_list = listdir(f"guilds/{str(vc.guild.id)}/channels/")
if f"{str(vc.id)}.json" in channels_list: if f"{str(vc.id)}.json" in channels_list:
return True return True
else: else:
@@ -207,98 +200,98 @@ def isUserVoice(vc):
except: except:
return False return False
async def removeUserVoice(vc): async def removeUserVoice(vc: VoiceChannel) -> None:
global path
global debug global debug
channels_list = os.listdir(f"{path}/guilds/{str(vc.guild.id)}/channels/") channels_list = listdir(f"guilds/{str(vc.guild.id)}/channels/")
if f"{vc.id}.json" in channels_list: if f"{vc.id}.json" in channels_list:
vc_file = f"{path}/guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json" vc_file = f"guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json"
vc_conf = loadJson(vc_file) vc_conf = loadJson(vc_file)
needed_channel = discord.utils.get(vc.guild.channels, id=vc.id) needed_channel = utils.get(vc.guild.channels, id=vc.id)
nomic_channel = discord.utils.get(vc.guild.channels, id=vc_conf["nomic"]) if loadJson("config.json")["enable_nomic"]:
nomic_channel = utils.get(vc.guild.channels, id=vc_conf["nomic"])
os.remove(vc_file) remove(vc_file)
await needed_channel.delete() await needed_channel.delete()
if debug: if debug:
appendLog(f"Removed voice channel '{needed_channel}' ({str(needed_channel.id)}) of user with id {str(vc_conf['ownerid'])}", guild=vc.guild) appendLog(f"Removed voice channel '{needed_channel}' ({str(needed_channel.id)}) of user with id {str(vc_conf['ownerid'])}", guild=vc.guild)
else: else:
appendLog(f"Removed voice channel '{needed_channel}' of user with id {str(vc_conf['ownerid'])}", guild=vc.guild) appendLog(f"Removed voice channel '{needed_channel}' of user with id {str(vc_conf['ownerid'])}", guild=vc.guild)
await nomic_channel.delete()
if debug: if loadJson("config.json")["enable_nomic"]:
appendLog(f"Removed nomic channel {nomic_channel} ({str(nomic_channel.id)}) of channel with id {str(needed_channel.id)}", guild=vc.guild) await nomic_channel.delete()
else: if debug:
appendLog(f"Removed nomic channel '{nomic_channel}' of channel with id {str(needed_channel.id)}", guild=vc.guild) appendLog(f"Removed nomic channel {nomic_channel} ({str(nomic_channel.id)}) of channel with id {str(needed_channel.id)}", guild=vc.guild)
else:
appendLog(f"Removed nomic channel '{nomic_channel}' of channel with id {str(needed_channel.id)}", guild=vc.guild)
else: else:
return return
async def createUserVoice(vc, category, member): async def createUserVoice(vc: VoiceChannel, category: CategoryChannel, member: Member) -> VoiceChannel:
global path
global debug global debug
chan = {} chan = {}
overwrites_channel = { overwrites_channel = {
vc.guild.default_role: discord.PermissionOverwrite(view_channel=True), vc.guild.default_role: PermissionOverwrite(view_channel=True),
vc.guild.me: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True), vc.guild.me: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True),
member: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True) member: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True)
} }
overwrites_nomic = { overwrites_nomic = {
vc.guild.default_role: discord.PermissionOverwrite(view_channel=False, read_messages=False), vc.guild.default_role: PermissionOverwrite(view_channel=False, read_messages=False),
vc.guild.me: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True), vc.guild.me: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True),
member: discord.PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True) member: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True)
} }
created_channel = await vc.guild.create_voice_channel(getMsg("name_voice", vc.guild).format(member.name), category=category, overwrites=overwrites_channel) created_channel = await vc.guild.create_voice_channel(getMsg("name_voice", vc.guild).format(member.name), category=category, overwrites=overwrites_channel)
if debug: if debug:
appendLog(f"Created voice channel '{created_channel}' ({str(created_channel.id)}) for user {member} ({str(member.id)})", guild=vc.guild) appendLog(f"Created voice channel '{created_channel}' ({str(created_channel.id)}) for user {member} ({str(member.id)})", guild=vc.guild)
else: else:
appendLog(f"Created voice channel '{created_channel}' for user {member}", guild=vc.guild) appendLog(f"Created voice channel '{created_channel}' for user {member}", guild=vc.guild)
if not os.path.isdir(f"{path}/guilds/{str(created_channel.guild.id)}/channels"): if not path.isdir(f"guilds/{str(created_channel.guild.id)}/channels"):
os.mkdir(f"{path}/guilds/{str(created_channel.guild.id)}/channels") makedirs(f"guilds/{str(created_channel.guild.id)}/channels", exist_ok=True)
vc_file = f"{path}/guilds/{str(created_channel.guild.id)}/channels/{str(created_channel.id)}.json" vc_file = f"guilds/{str(created_channel.guild.id)}/channels/{str(created_channel.id)}.json"
chan["ownerid"] = member.id chan["ownerid"] = member.id
saveJson(chan, vc_file) saveJson(chan, vc_file)
nomic_channel = await vc.guild.create_text_channel(getMsg("name_nomic", vc.guild).format(created_channel.id), category=category, overwrites=overwrites_nomic, topic=getMsg("description_nomic", vc.guild).format(str(created_channel.id))) if loadJson("config.json")["enable_nomic"]:
if debug: nomic_channel = await vc.guild.create_text_channel(getMsg("name_nomic", vc.guild).format(created_channel.id), category=category, overwrites=overwrites_nomic, topic=getMsg("description_nomic", vc.guild).format(str(created_channel.id)))
appendLog(f"Created nomic channel '{nomic_channel}' ({str(nomic_channel.id)}) for channel '{created_channel}' ({str(created_channel.id)})", guild=vc.guild) if debug:
else: appendLog(f"Created nomic channel '{nomic_channel}' ({str(nomic_channel.id)}) for channel '{created_channel}' ({str(created_channel.id)})", guild=vc.guild)
appendLog(f"Created nomic channel '{nomic_channel}' for channel '{created_channel}'", guild=vc.guild) else:
chan["nomic"] = nomic_channel.id appendLog(f"Created nomic channel '{nomic_channel}' for channel '{created_channel}'", guild=vc.guild)
saveJson(chan, vc_file) chan["nomic"] = nomic_channel.id
saveJson(chan, vc_file)
return created_channel return created_channel
def isVoiceOfUser(vc, member): def isVoiceOfUser(vc: VoiceChannel, member: Member) -> bool:
global path vc_file = f"guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json"
vc_file = f"{path}/guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json"
vc_conf = loadJson(vc_file) vc_conf = loadJson(vc_file)
if vc_conf["ownerid"] == member.id: if vc_conf["ownerid"] == member.id:
return True return True
else: else:
return False return False
async def changeNomicPerms(mode, vc, member): async def changeNomicPerms(mode: Literal["deny", "allow"], vc: VoiceChannel, member: Member) -> None:
global path vc_file = f"guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json"
vc_file = f"{path}/guilds/{str(vc.guild.id)}/channels/{str(vc.id)}.json"
vc_conf = loadJson(vc_file) vc_conf = loadJson(vc_file)
nomic_channel = discord.utils.get(vc.guild.channels, id=vc_conf["nomic"]) if loadJson("config.json")["enable_nomic"]:
if mode == "deny": nomic_channel = utils.get(vc.guild.channels, id=vc_conf["nomic"])
await nomic_channel.set_permissions(member, view_channel=False) if mode == "deny":
else: await nomic_channel.set_permissions(member, view_channel=False)
await nomic_channel.set_permissions(member, view_channel=True) else:
await nomic_channel.set_permissions(member, view_channel=True)
async def clearTrash(client): async def clearTrash(client: Client):
global path if not path.isdir(f"guilds/"):
if not os.path.isdir(f"{path}/guilds/"): makedirs(f"guilds", exist_ok=True)
os.mkdir(f"{path}/guilds") guilds_list = listdir(f"guilds/")
guilds_list = os.listdir(f"{path}/guilds/")
for guild in guilds_list: for guild in guilds_list:
guild_object = client.get_guild(int(guild)) guild_object = client.get_guild(int(guild))
if os.path.isdir(f"{path}/guilds/{guild}/channels"): if path.isdir(f"guilds/{guild}/channels"):
channels_list = os.listdir(f"{path}/guilds/{guild}/channels/") channels_list = listdir(f"guilds/{guild}/channels/")
for channel in channels_list: for channel in channels_list:
channel_id = channel[:-5] channel_id = channel[:-5]
try: try:
selected_channel = discord.utils.get(guild_object.voice_channels, id=int(channel_id)) selected_channel = utils.get(guild_object.voice_channels, id=int(channel_id))
channel_owner = loadJson(f"{path}/guilds/{guild}/channels/{channel}")["ownerid"] channel_owner = loadJson(f"guilds/{guild}/channels/{channel}")["ownerid"]
remove_channel = True remove_channel = True
for member in selected_channel.members: for member in selected_channel.members:
if member.id == channel_owner: if member.id == channel_owner:
@@ -306,12 +299,12 @@ async def clearTrash(client):
if remove_channel: if remove_channel:
await removeUserVoice(selected_channel) await removeUserVoice(selected_channel)
except: except:
os.remove(f"{path}/guilds/{guild}/channels/{channel_id}.json") remove(f"guilds/{guild}/channels/{channel_id}.json")
#async def autoClearTrash(client): #async def autoClearTrash(client):
# execute clearTrash every 120 seconds # execute clearTrash every 120 seconds
def getHelpMessage(ctx, version): def getHelpMessage(ctx: ApplicationContext, version: float) -> Embed:
#channelExists(number, guild, type="Voice") #channelExists(number, guild, type="Voice")
@@ -332,9 +325,9 @@ def getHelpMessage(ctx, version):
description = "\n".join([desc_locale, desc_channel, desc_category]) description = "\n".join([desc_locale, desc_channel, desc_category])
embed=discord.Embed(title=getMsg("help_title", ctx.guild), description=description, color=strToColor(config["color_default"])) embed=Embed(title=getMsg("help_title", ctx.guild), description=description, color=strToColor(config["color_default"]))
else: else:
embed=discord.Embed(title=getMsg("help_title_dm", ctx.guild), color=strToColor(config["color_default"])) embed=Embed(title=getMsg("help_title_dm", ctx.guild), color=strToColor(config["color_default"]))
embed.set_author(name=f'{config["bot_name"]} v{str(version)}', url=config["bot_site"], icon_url=config["bot_icon"]) embed.set_author(name=f'{config["bot_name"]} v{str(version)}', url=config["bot_site"], icon_url=config["bot_icon"])
@@ -352,7 +345,7 @@ def getHelpMessage(ctx, version):
return embed return embed
async def guildConfigured(guild): async def guildConfigured(guild: Guild) -> str:
output = {} output = {}
config = loadJson("config.json") config = loadJson("config.json")
@@ -360,7 +353,7 @@ async def guildConfigured(guild):
for kind in ["channel", "category"]: for kind in ["channel", "category"]:
if guildConfGet(guild, kind) is not None: if guildConfGet(guild, kind) is not None:
try: try:
guild_object = discord.utils.get(guild.categories, id=guildConfGet(guild, kind)) guild_object = utils.get(guild.categories, id=guildConfGet(guild, kind))
output[kind] = getMsg("configured_"+kind, guild).format(guild_object.name) output[kind] = getMsg("configured_"+kind, guild).format(guild_object.name)
except Exception as exp: except Exception as exp:
output[kind] = getMsg("unconfigured_"+kind, guild) output[kind] = getMsg("unconfigured_"+kind, guild)

View File

@@ -4,52 +4,52 @@
"locale_name": "Українська", "locale_name": "Українська",
"name_voice": "Канал {0}", "name_voice": "Канал {0}",
"name_nomic": "без-мікро-{0}", "name_nomic": "без-мікро-{0}",
"description_nomic": "Текстовий канал для комунікації без мікрофона\nID голосової кімнати: {0}", "description_nomic": "Текстовий канал для комунікації без мікрофона\\nID голосової кімнати: {0}",
"set_channel_title": "☑ Твірний канал встановлено", "set_channel_title": "☑ Твірний канал встановлено",
"set_channel_description": "Голосовий канал `{0}` був встановлений як твірний.", "set_channel_description": "Голосовий канал `{0}` був встановлений як твірний.",
"set_category_title": "☑ Твірну категорію встановлено", "set_category_title": "☑ Твірну категорію встановлено",
"set_category_description": "Категорія `{0}` була встановлена як твірна.", "set_category_description": "Категорія `{0}` була встановлена як твірна.",
"set_locale_title": "☑ Мова бота встановлена", "set_locale_title": "☑ Мова бота встановлена",
"set_locale_description": "На цьому сервері тепер використовується `{0}`", "set_locale_description": "На цьому сервері тепер використовується `{0}`",
"reset_channel_title": "☑ Твірний канал скинутий", "reset_channel_title": "☑ Твірний канал скинутий",
"reset_channel_description": "Для вибору нового каналу використовуйте `/channel set`", "reset_channel_description": "Для вибору нового каналу використовуйте `/channel set`",
"reset_category_title": "☑ Твірна категорія скинута", "reset_category_title": "☑ Твірну категорію скинуто",
"reset_category_description": "Для вибору нової категорії використовуйте `/category set`", "reset_category_description": "Використовуйте `/category set` для вибору нової категорії",
"reset_locale_title": "☑ Мова бота скинута", "reset_locale_title": "☑ Мова бота скинута",
"reset_locale_description": "Тепер використовується стандартна мова `{0}`.\nДля вибору іншого використовуйте `/locale set`", "reset_locale_description": "Тепер використовується стандартна мова `{0}`.\\nДля вибору іншого використовуйте `/locale set`",
"error_channel_title": "🚫 Помилка установки каналу", "error_channel_title": "🚫 Помилка встановлення каналу",
"error_channel_description": "Правильне використання: `/channel set`", "error_channel_description": "Правильне використання: `/channel set`",
"error_category_title": "🚫 Помилка установки категорії", "error_category_title": "🚫 Помилка налаштування категорії",
"error_category_description": "Правильне використання: `/category set`", "error_category_description": "Правильне використання: `/category set`",
"error_locale_title": "🚫 Помилка встановлення мови", "error_locale_title": "🚫 Помилка встановлення мови",
"error_locale_description": "Правильне використання: `/locale set`\nДоступні мови: {1}", "error_locale_description": "Правильне використання: `/locale set`\\оступні мови: {1}",
"hint_none_channel_title": "⚠ Твірний канал не заданий", "hint_none_channel_title": "⚠ Твірний канал не заданий",
"hint_none_channel_description": "Для правильної роботи робота необхідно задати твірний канал, входячи в який користувач зможе створити свій приватний голосовий канал. Встановіть твірний канал командою `/channel set`", "hint_none_channel_description": "Для правильної роботи робота необхідно задати твірний канал, входячи в який користувач зможе створити свій приватний голосовий канал. Встановіть твірний канал командою `/channel set`",
"hint_none_category_title": "⚠ Твірна категорія не задана", "hint_none_category_title": "⚠ Твірна категорія не встановлена",
"hint_none_category_description": "Для правильної роботи робота необхідно задати твірну категорію, всередині якої для користувача буде створено його приватний канал. Встановіть твірну категорію командою `/category set`", "hint_none_category_description": "Для правильної роботи робота необхідно задати твірну категорію, всередині якої для користувача буде створено його приватний канал. Встановіть твірну категорію командою `/category set`",
"hint_none_locale_title": "⚠ Мова бота не задана", "hint_none_locale_title": "⚠ Мова бота не встановлена",
"hint_none_locale_description": "На цьому сервері використовується стандартний `{0}`.\nЩоб змінити його використовуйте `/locale set`", "hint_none_locale_description": "На цьому сервері використовується стандартний `{0}`.\\об змінити його використовуйте `/locale set`",
"error_text_channel_title": "🚫 Помилка установки каналу", "error_text_channel_title": "🚫 Помилка встановлення каналу",
"error_text_channel_description": "Вибраний канал повинен бути голосовим.", "error_text_channel_description": "Обраний канал має бути голосовим каналом.",
"help_title": "Інформація про сервер", "help_title": "Інформація про сервер",
"help_title_dm": "Команда бота", "help_title_dm": "Команда бота",
"help_server": "Команди повинні виконуватись на сервері", "help_server": "Команди слід запускати на сервері",
"help_locale": " Мова бота на сервері: `{0}`", "help_locale": " Мова бота на сервері: `{0}`",
"help_channel_set": "☑ Твірний канал: `{0}`", "help_channel_set": "☑ Твірний канал: `{0}`",
"help_channel_none": "⚠ Твірний канал не встановлено", "help_channel_none": "⚠ Твірний канал не встановлено",
"help_category_set": "☑ Твірна категорія: `{0}`", "help_category_set": "☑ Твірна категорія: `{0}`",
"help_category_none": "⚠ Твірна категорія не встановлена", "help_category_none": "⚠ Твірна категорія не встановлена",
"help_cmd_shutdown": "Вимкнути бота", "help_cmd_shutdown": "Вимкнути бота",
"help_cmd_channel": "Встановити твірний канал\nКанал повинен бути голосовим", "help_cmd_channel": "Встановити твірний канал\\nКанал повинен бути голосовим",
"help_cmd_category": "Встановити твірну категорію", "help_cmd_category": "Встановити твірну категорію",
"help_cmd_locale": "Змінити мову бота на сервері", "help_cmd_locale": "Змінити мову бота на сервері",
"help_notice_id": "Зверніть увагу, що ім'я каналу ≠ ID каналу", "help_notice_id": "Зверніть увагу, що ім'я каналу ≠ ID каналу",
"help_notice_id_category": "Зверніть увагу, що ім'я категорії ≠ ID категорії", "help_notice_id_category": "Зверніть увагу, що ім'я категорії ≠ ID категорії",
"forbidden_title": "🚫 Відмовлено у доступі", "forbidden_title": "🚫 Немає прав доступу",
"forbidden_description": "Для налаштування приватних каналів потрібно мати право **Адміністратор**", "forbidden_description": "Для налаштування приватних каналів потрібно мати право **Адміністратор**",
"dm_title": "🚫 Помилка виконання", "dm_title": "🚫 Помилка виконання",
"dm_description": "Команди можна виконувати лише перебуваючи на сервері", "dm_description": "Команди можна виконувати лише перебуваючи на сервері",
"admin_title": "🚫 Відмовлено в доступі", "admin_title": "🚫 Немає прав доступу",
"admin_description": "Треба бути адміністратором бота щоб зробити це" "admin_description": "Треба бути адміністратором бота щоб зробити це"
} }
} }

View File

@@ -0,0 +1,2 @@
ujson~=5.7.0
py-cord[speed]~=2.3.2

View File

@@ -1 +1,2 @@
py-cord>=2.0.0b7 py-cord~=2.3.2
requests~=2.28.2

View File

@@ -1,36 +1,41 @@
import os
import sys
import json
import shutil
import requests
import threading
try: try:
import discord # type: ignore from requests import get
from discord import ApplicationContext, Option, Intents # type: ignore from discord import ApplicationContext, Intents, Bot, ActivityType, Activity, VoiceState
except Exception as exp: except Exception as exp:
print(f"Module py-cord is not installed. Make sure to run 'pip install -r requirements.txt' before first start") print(f"Dependencies not installed. Make sure to run 'pip install -r requirements.txt' before first start")
sys.exit() exit()
from os import getpid
from shutil import rmtree
from functions import * from functions import *
pid = os.getpid()
version = 1.5 pid = getpid()
version = 1.8
if loadJson("config.json")["owner"] == "SET-OWNER-ID" or loadJson("config.json")["bot_token"] == "SET-BOT-TOKEN": if loadJson("config.json")["owner"] == "SET-OWNER-ID" or loadJson("config.json")["bot_token"] == "SET-BOT-TOKEN":
print(f"Bot is not correctly configured.\nMake sure you've set up owner id and bot token in {path}/config.json\nLearn more here: https://github.com/profitrollgame/YusarinBot") print(f"Bot is not correctly configured.\nMake sure you've set up owner id and bot token in 'config.json'\nLearn more here: https://git.end-play.xyz/profitroll/YusarinBot")
sys.exit() exit()
if loadJson("config.json")["check_for_updates"]: if loadJson("config.json")["check_for_updates"]:
try: try:
serv_ver = json.loads(requests.get("https://api.end-play.xyz/version&apikey=publickey&app=yusarinbot").text)["version"] serv_ver = loads(get("https://api.end-play.xyz/version?app=yusarinbot&apikey=publickey").text)["version"]
if float(serv_ver) > version: if float(serv_ver) > version:
appendLog(f"YusarinBot version {serv_ver} is available. Download new version here: https://github.com/profitrollgame/YusarinBot/releases/latest") appendLog(f"YusarinBot version {serv_ver} is available. Download new version here: https://git.end-play.xyz/profitroll/YusarinBot/releases/latest")
appendLog(f"Currently using YusarinBot v{str(version)}") appendLog(f"Currently using YusarinBot v{str(version)}")
except Exception as exp: except Exception as exp:
appendLog(f"Could not get YusarinBot cloud version due to {exp}. Currently using {str(version)}") appendLog(f"Could not get YusarinBot cloud version due to {exp}. Currently using {str(version)}")
intents = Intents().all() intents = Intents().all()
client = discord.Bot(intents=intents) client = Bot(intents=intents)
from cogs.cogCategory import CogCategory
from cogs.cogChannel import CogChannel
from cogs.cogLocale import CogLocale
from cogs.cogUtility import CogUtility
@client.slash_command(name="help", description="Get information about this server")
async def help(ctx: ApplicationContext):
await ctx.respond(embed=getHelpMessage(ctx, version))
@client.event @client.event
async def on_ready(): async def on_ready():
@@ -39,183 +44,14 @@ async def on_ready():
config = loadJson("config.json") config = loadJson("config.json")
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=config["bot_activity"])) await client.change_presence(activity=Activity(type=ActivityType.listening, name=config["bot_activity"]))
await clearTrash(client) await clearTrash(client)
@client.event client.add_cog(CogCategory(client))
async def on_guild_join(guild): client.add_cog(CogChannel(client))
client.add_cog(CogLocale(client))
global path client.add_cog(CogUtility(client))
os.mkdir(f"{path}/guilds/{str(guild.id)}")
os.mkdir(f"{path}/guilds/{str(guild.id)}/channels")
saveJson({}, f"{path}/guilds/{str(guild.id)}/config.json")
appendLog(f"Joined guild '{guild}' with id {str(guild.id)}")
@client.event
async def on_guild_remove(guild):
global path
try:
shutil.rmtree(f"{path}/guilds/{str(guild.id)}")
except:
pass
appendLog(f"Left guild '{guild}' with id {str(guild.id)}")
@client.event
async def on_voice_state_update(member, before, after):
global debug
config = loadJson("config.json")
vc_from = before.channel
vc_to = after.channel
# If user left vc
if vc_to is None:
if isUserVoice(vc_from):
if isVoiceOfUser(vc_from, member):
await removeUserVoice(vc_from)
return
else:
await changeNomicPerms("deny", vc_from, member)
# If user joined vc
else:
if isUserVoice(vc_from):
if isVoiceOfUser(vc_from, member):
await removeUserVoice(vc_from)
else:
await changeNomicPerms("deny", vc_from, member)
if isUserVoice(vc_to):
await changeNomicPerms("allow", vc_to, member)
if vc_to.id == guildConfGet(vc_to.guild, "channel"):
if guildConfGet(vc_to.guild, "category") is not None:
voice_chan = await createUserVoice(vc_to, discord.utils.get(vc_to.guild.categories, id=guildConfGet(vc_to.guild, "category")), member)
try:
await member.move_to(voice_chan)
except:
await removeUserVoice(voice_chan)
else:
if debug:
appendLog(f"Category for guild {vc_to.guild} ({str(vc_to.guild.id)}) is not set", guild=vc_to.guild)
else:
appendLog(f"Category for guild {vc_to.guild} is not set", guild=vc_to.guild)
#=========================================================================================================================
@client.slash_command(name="shutdown", description="Restart the bot")
async def shutdown(ctx: ApplicationContext):
config = loadJson("config.json")
if ctx.author.id == config["owner"]:
await ctx.respond(embed=makeEmbed(description=getMsg("shutdown", ctx.guild).format(ctx.author), color=strToColor(config["color_default"])))
os.system(f"kill -9 {str(pid)}")
else:
await ctx.respond(embed=makeEmbed(title=getMsg("admin_title", ctx.guild), description=getMsg("admin_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================
#=========================================================================================================================
@client.slash_command(name="help", description="Get information about this server")
async def help(ctx: ApplicationContext):
await ctx.respond(embed=getHelpMessage(ctx, version))
#=========================================================================================================================
#=========================================================================================================================
locale = client.create_group("locale", "Commands related to bot's locale")
valid_locales = []
files_locales = os.listdir(f"{path}/locale/")
for entry in files_locales:
valid_locales.append(".".join(entry.split(".")[:-1]))
@locale.command(name="set", description="Set bot's messages language")
async def locale_set(ctx: ApplicationContext, language: Option(str, "One of the languages in list", choices=valid_locales)): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if language+".json" in os.listdir(f"{path}/locale/"):
guildConfSet(ctx.guild, "locale", language)
appendLog(f"Server's locale is now set to {language}", ctx.guild)
await ctx.respond(embed=makeEmbed(title=getMsg("set_locale_title", ctx.guild), description=getMsg("set_locale_description", ctx.guild).format(getMsg("locale_name", ctx.guild)), color=strToColor(config["color_ok"])))
else:
valid_locales = []
files_locales = os.listdir(f"{path}/locale/")
for entry in files_locales:
valid_locales.append(entry.split(".")[:-1])
await ctx.respond(embed=makeEmbed(title=getMsg("error_locale_title", ctx.guild), description=getMsg("error_locale_description", ctx.guild).format(", ".join(valid_locales)), color=strToColor(config["color_error"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
@locale.command(name="reset", description="Reset the bot's language in this guild")
async def locale_reset(ctx: ApplicationContext): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if guildConfGet(ctx.guild, "locale") is not None:
guildConfReset(ctx.guild, "locale")
appendLog(f"Server's locale has been reset", ctx.guild)
await ctx.respond(embed=makeEmbed(title=getMsg("reset_locale_title", ctx.guild), description=getMsg("reset_locale_description", ctx.guild).format(getMsg("locale_name", ctx.guild)), color=strToColor(config["color_ok"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_locale_title", ctx.guild), description=getMsg("hint_none_locale_description", ctx.guild).format(getMsg("locale_name", ctx.guild)), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================
#=========================================================================================================================
channel = client.create_group("channel", "Commands related to parent voice channel")
@channel.command(name="set", description="Select the voice channel that will be parent to private ones")
async def channel_set(ctx: ApplicationContext, channel: Option(discord.VoiceChannel, "Parent Voice Channel")): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
guildConfSet(ctx.guild, "channel", channel.id)
await ctx.respond(embed=makeEmbed(title=getMsg("set_channel_title", ctx.guild), description=getMsg("set_channel_description", ctx.guild).format(channel.name), color=strToColor(config["color_ok"])))
if guildConfGet(ctx.guild, "category") is None:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_category_title", ctx.guild), description=getMsg("hint_none_category_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
@channel.command(name="reset", description="Reset the currently selected parent voice channel")
async def channel_reset(ctx: ApplicationContext): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if guildConfGet(ctx.guild, "channel") is not None:
guildConfReset(ctx.guild, "channel")
await ctx.respond(embed=makeEmbed(title=getMsg("reset_channel_title", ctx.guild), description=getMsg("reset_channel_description", ctx.guild), color=strToColor(config["color_ok"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_channel_title", ctx.guild), description=getMsg("hint_none_channel_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================
#=========================================================================================================================
category = client.create_group("category", "Commands related to parent channels category")
@category.command(name="set", description="Select the voice channel that will be parent to private ones")
async def category_set(ctx: ApplicationContext, category: Option(discord.CategoryChannel, "Parent Channel Category")): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
guildConfSet(ctx.guild, "category", category.id)
await ctx.respond(embed=makeEmbed(title=getMsg("set_category_title", ctx.guild), description=getMsg("set_category_description", ctx.guild).format(category.name), color=strToColor(config["color_ok"])))
if guildConfGet(ctx.guild, "channel") is None:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_channel_title", ctx.guild), description=getMsg("hint_none_channel_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
@category.command(name="reset", description="Reset the currently selected parent channel category")
async def category_reset(ctx: ApplicationContext): # type: ignore
config = loadJson("config.json")
if ctx.guild is not None:
if guildConfGet(ctx.guild, "category") is not None:
guildConfReset(ctx.guild, "category")
await ctx.respond(embed=makeEmbed(title=getMsg("reset_category_title", ctx.guild), description=getMsg("reset_category_description", ctx.guild), color=strToColor(config["color_ok"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("hint_none_category_title", ctx.guild), description=getMsg("hint_none_category_description", ctx.guild), color=strToColor(config["color_warn"])))
else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"])))
#=========================================================================================================================
appendLog(f"Trying to log in...") appendLog(f"Trying to log in...")
client.run(loadJson("config.json")["bot_token"]) client.run(loadJson("config.json")["bot_token"])