Compare commits

...

42 Commits
v1.8 ... main

Author SHA1 Message Date
Profitroll fa0cd2e30f Merge pull request 'Update dependency ujson to v5.10.0' (#9) from renovate/ujson-5.x into main
Reviewed-on: #9
2024-05-14 23:57:39 +03:00
Renovate 73d6b01ede Update dependency ujson to v5.10.0 2024-05-14 05:39:22 +03:00
Profitroll 5c5a620d65 Merge pull request 'Update dependency py-cord to v2.5.0' (#8) from renovate/py-cord-2.x into main
Reviewed-on: #8
2024-03-02 17:44:44 +02:00
Renovate 1c05efa0f4 Update dependency py-cord to v2.5.0 2024-03-02 11:01:22 +02:00
Profitroll f5de2457ba Merge pull request 'Update dependency ujson to v5.9.0' (#7) from renovate/ujson-5.x into main
Reviewed-on: #7
2023-12-12 01:28:19 +02:00
Renovate 8cec182435 Update dependency ujson to v5.9.0 2023-12-11 01:34:57 +02:00
Profitroll ad0ab65c5d Fixed http 2023-08-26 00:00:44 +03:00
Profitroll 0029cdaa30 Replaced Crowdin with Weblate 2023-08-25 23:59:50 +03:00
Profitroll e94883f6ad Merge pull request 'Update dependency ujson to v5.8.0' (#6) from renovate/ujson-5.x into main
Reviewed-on: #6
2023-08-07 14:13:08 +03:00
Renovate 465838a196 Update dependency ujson to v5.8.0 2023-08-07 14:10:43 +03:00
Profitroll a059fb0518 Merge pull request 'Update dependency requests to v2.31.0' (#5) from renovate/requests-2.x into main
Reviewed-on: #5
2023-05-22 19:39:04 +03:00
Renovate c4ee74753b Update dependency requests to v2.31.0 2023-05-22 18:40:49 +03:00
Profitroll 492edc242f Merge pull request 'Update dependency requests to v2.30.0' (#4) from renovate/requests-2.x into main
Reviewed-on: #4
2023-05-08 11:50:52 +03:00
Renovate 66c0ad7ee8 Update dependency requests to v2.30.0 2023-05-08 11:46:39 +03:00
Profitroll c1ee1b50ab Added config check and updated version to 2.3 2023-05-07 16:39:48 +02:00
Profitroll 7a379cf97e Updated ignore 2023-05-07 16:38:46 +02:00
Profitroll 388e27fdff config.json is now config_example.json 2023-05-07 16:38:17 +02:00
Profitroll 65a0e1097e Updated version to 2.2 2023-05-07 16:24:09 +02:00
Profitroll 3fe67e18b0 Channel will terminate when the last user quit 2023-05-07 16:23:16 +02:00
Profitroll 91eecb4b0b Fixed channels logic 2023-05-02 16:13:11 +02:00
Profitroll 740c6a1464 Updated version to 2.1 2023-05-02 16:10:32 +02:00
Profitroll 3ca44dcd2d Updated version to 2.0 2023-05-02 15:10:29 +02:00
Profitroll 301439f144 Fixed bot trigger on deafen and mute 2023-05-02 15:09:23 +02:00
Profitroll 3435d59d96 Merge pull request 'Update dependency requests to v2.29.0' (#3) from renovate/requests-2.x into main
Reviewed-on: #3
2023-04-26 23:49:52 +03:00
Renovate d560bce8c6 Update dependency requests to v2.29.0 2023-04-26 18:48:15 +03:00
Profitroll 528604b6b4 Made a few dependencies strict 2023-04-23 10:35:27 +02:00
Profitroll 76c7812284 Renamed Renovate config 2023-04-20 13:38:00 +02:00
Profitroll 8e48334cea Merge pull request 'Configure Renovate' (#1) from renovate/configure into main
Reviewed-on: #1
2023-04-20 14:37:19 +03:00
Renovate bc9cebbec6 Add renovate.json 2023-04-20 14:13:46 +03:00
Profitroll a178aa2af9 Merge branch 'main' of https://git.profitroll.eu/profitroll/YusarinBot 2023-04-19 14:27:12 +02:00
Profitroll fb3e291d77 Sorted imports and removed unused ones 2023-04-19 14:23:41 +02:00
Profitroll 75c1889477 Formatted everything with black 2023-04-19 14:21:39 +02:00
Profitroll 5329b4c727 Bump pycord to 2.4.1 2023-04-19 14:16:32 +02:00
Profitroll 0f3a6121d6 Fixed black badge link 2023-03-22 21:30:45 +01:00
Profitroll 7f1f86ca03 Fixed update checker behavior 2023-03-22 21:26:05 +01:00
Profitroll c600747848 Bump py-cord to 2.4.1 2023-03-22 21:09:58 +01:00
Profitroll a2bd55dcdf Updated version to 1.9 2023-03-22 21:09:32 +01:00
Profitroll 98ac531a36 Changed version API requests 2023-03-22 21:09:09 +01:00
Profitroll af24a055b6 Updated header 2023-03-22 21:05:46 +01:00
Profitroll 97fe10bb96 Formatted with black 2023-03-22 20:59:50 +01:00
Profitroll bad83b0266 Added gitignore 2023-03-22 20:56:01 +01:00
Profitroll 97d05866e2 Updated default config link 2023-01-18 11:23:28 +02:00
12 changed files with 759 additions and 189 deletions

173
.gitignore vendored Normal file
View File

@ -0,0 +1,173 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# ---> VisualStudioCode
.vscode/*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Project
venv
venv_linux
venv_windows
config.json

6
.renovaterc Normal file
View File

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}

View File

@ -1,5 +1,19 @@
# YusarinBot <h1 align="center">YusarinBot</h1>
[![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)
<p align="center">
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img alt="License: GPL v2" src="https://img.shields.io/badge/License-GPL_v2-blue.svg">
</a>
<a href="https://weblate.end-play.xyz/engage/yusarinbot/">
<img src="https://weblate.end-play.xyz/widget/yusarinbot/svg-badge.svg" alt="Translation status" />
</a>
<a href="https://git.end-play.xyz/profitroll/YusarinBot">
<img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg">
</a>
<a href="https://discord.com/invite/2zpzErtnNT">
<img alt="Discord" src="https://img.shields.io/discord/940760922346123284">
</a>
</p>
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.
@ -8,30 +22,36 @@ Installation instructions are listed below. Please, make sure you have installed
> 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. > 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://git.end-play.xyz/profitroll/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. `cp config_example.json config.json`
5. `python yusarin.py` 5. `nano config.json`
6. `python yusarin.py`
## Installation (Detailed) ## Installation (Detailed)
1. Download and install Python 3.8+ 1. Download and install Python 3.8+
2. Download bot's release: https://git.end-play.xyz/profitroll/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. If you're using Linux system - install [dependencies for Pycord](https://docs.pycord.dev/en/stable/installing.html#installing) 5. If you're using Linux system - install [dependencies for Pycord](https://docs.pycord.dev/en/stable/installing.html#installing)
6. Run `pip install -r requirements.txt` to install dependencies 6. Run `pip install -r requirements.txt` to install dependencies
7. [Optionally] run `pip install -r requirements-optional.txt` to install optional dependencies 7. [Optionally] run `pip install -r requirements-optional.txt` to install optional dependencies
8. Create new Discord application at https://discord.com/developers/applications 8. Create new Discord application at <https://discord.com/developers/applications>
9. You can rename it however you want, set needed descriptions etc 9. You can rename it however you want, set needed descriptions etc
10. Go to "Bot" tab and enable application as bot 10. Go to "Bot" tab and enable application as bot
11. Copy token of your bot 11. Copy token of your bot
12. Open file `config.json` with your favorite text editor and paste your token as value of "bot_token" key 12. Copy `config_example.json` to `config.json`
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/) 13. Open file `config.json` with your favorite text editor and paste your token as value of "bot_token" key
14. Bot is ready! Run it using `python yusarin.py` 14. Copy your own Discord ID and paste it as value of "owner" key (How to get ID: <https://support.playhive.com/discord-user-id/>)
15. Bot is ready! Run it using `python yusarin.py`
## Config explanation ## Config explanation
Default configuration file is [available here](https://git.end-play.xyz/profitroll/YusarinBot/blob/main/config.json)
Default configuration file is [available here](https://git.profitroll.eu/profitroll/YusarinBot/src/branch/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]
@ -51,6 +71,7 @@ Default configuration file is [available here](https://git.end-play.xyz/profitro
- "auto_clear_timer" - Option that defines how often "auto_clear_trash" will do its job [Integer] - "auto_clear_timer" - Option that defines how often "auto_clear_trash" will do its job [Integer]
## 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) 3. If you have questions/suggestions - just join our [Discord Support Server](https://discord.com/invite/2zpzErtnNT)

View File

@ -1,35 +1,91 @@
from discord import ApplicationContext, Option, SlashCommandGroup, CategoryChannel from discord import ApplicationContext, CategoryChannel, Option, SlashCommandGroup
from discord.ext import commands from discord.ext import commands
from functions import getMsg, guildConfGet, guildConfReset, guildConfSet, loadJson, makeEmbed, strToColor
#========================================================================================================================= from functions import (
getMsg,
guildConfGet,
guildConfReset,
guildConfSet,
loadJson,
makeEmbed,
strToColor,
)
# =========================================================================================================================
class CogCategory(commands.Cog): class CogCategory(commands.Cog):
def __init__(self, client): def __init__(self, client):
self.client = client self.client = client
category = SlashCommandGroup("category", "Commands related to parent channels category") 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") @category.command(
async def category_set(self, ctx: ApplicationContext, category: Option(CategoryChannel, "Parent Channel Category")): # type: ignore 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") config = loadJson("config.json")
if ctx.guild is not None: if ctx.guild is not None:
guildConfSet(ctx.guild, "category", category.id) 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"]))) 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: 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"]))) 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: else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"]))) 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") @category.command(
async def category_reset(self, ctx: ApplicationContext): # type: ignore name="reset", description="Reset the currently selected parent channel category"
)
async def category_reset(self, ctx: ApplicationContext): # type: ignore
config = loadJson("config.json") config = loadJson("config.json")
if ctx.guild is not None: if ctx.guild is not None:
if guildConfGet(ctx.guild, "category") is not None: if guildConfGet(ctx.guild, "category") is not None:
guildConfReset(ctx.guild, "category") 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"]))) 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: 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"]))) 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: else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"]))) await ctx.respond(
#========================================================================================================================= embed=makeEmbed(
title=getMsg("dm_title", ctx.guild),
description=getMsg("dm_description", ctx.guild),
color=strToColor(config["color_error"]),
)
)
# =========================================================================================================================

View File

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

View File

@ -1,11 +1,22 @@
from os import listdir from os import listdir
from discord import ApplicationContext, Option, SlashCommandGroup from discord import ApplicationContext, Option, SlashCommandGroup
from discord.ext import commands from discord.ext import commands
from functions import appendLog, getMsg, guildConfGet, guildConfReset, guildConfSet, loadJson, makeEmbed, strToColor
#========================================================================================================================= from functions import (
appendLog,
getMsg,
guildConfGet,
guildConfReset,
guildConfSet,
loadJson,
makeEmbed,
strToColor,
)
# =========================================================================================================================
class CogLocale(commands.Cog): class CogLocale(commands.Cog):
def __init__(self, client): def __init__(self, client):
self.client = client self.client = client
@ -17,32 +28,78 @@ class CogLocale(commands.Cog):
valid_locales.append(".".join(entry.split(".")[:-1])) valid_locales.append(".".join(entry.split(".")[:-1]))
@locale.command(name="set", description="Set bot's messages language") @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 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") config = loadJson("config.json")
if ctx.guild is not None: if ctx.guild is not None:
if language+".json" in listdir(f"locale/"): if language + ".json" in listdir(f"locale/"):
guildConfSet(ctx.guild, "locale", language) guildConfSet(ctx.guild, "locale", language)
appendLog(f"Server's locale is now set to {language}", ctx.guild) 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"]))) 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: else:
valid_locales = [] valid_locales = []
files_locales = listdir(f"locale/") files_locales = listdir(f"locale/")
for entry in files_locales: for entry in files_locales:
valid_locales.append(entry.split(".")[:-1]) 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"]))) 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: else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"]))) 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") @locale.command(name="reset", description="Reset the bot's language in this guild")
async def locale_reset(self, ctx: ApplicationContext): # type: ignore async def locale_reset(self, ctx: ApplicationContext): # type: ignore
config = loadJson("config.json") config = loadJson("config.json")
if ctx.guild is not None: if ctx.guild is not None:
if guildConfGet(ctx.guild, "locale") is not None: if guildConfGet(ctx.guild, "locale") is not None:
guildConfReset(ctx.guild, "locale") guildConfReset(ctx.guild, "locale")
appendLog(f"Server's locale has been reset", ctx.guild) 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"]))) 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: 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"]))) 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: else:
await ctx.respond(embed=makeEmbed(title=getMsg("dm_title", ctx.guild), description=getMsg("dm_description", ctx.guild), color=strToColor(config["color_error"]))) await ctx.respond(
#========================================================================================================================= embed=makeEmbed(
title=getMsg("dm_title", ctx.guild),
description=getMsg("dm_description", ctx.guild),
color=strToColor(config["color_error"]),
)
)
# =========================================================================================================================

View File

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

View File

@ -1,14 +1,27 @@
from gzip import open as gzipopen
from datetime import datetime from datetime import datetime
from os import listdir, makedirs, remove, stat, path from gzip import open as gzipopen
from os import listdir, makedirs, path, remove, stat
from shutil import copyfileobj from shutil import copyfileobj
from typing import Any, Literal, Union from typing import Any, Literal, Union
from discord import utils, Embed, TextChannel, VoiceChannel, PermissionOverwrite, Guild, CategoryChannel, Member, Client, ApplicationContext, Message
from discord import (
ApplicationContext,
CategoryChannel,
Client,
Embed,
Guild,
Member,
Message,
PermissionOverwrite,
TextChannel,
VoiceChannel,
utils,
)
try: try:
from ujson import loads, dumps from ujson import dumps, loads
except ImportError: except ImportError:
from json import loads, dumps from json import dumps, loads
# path = Path(__file__).resolve().parent # path = Path(__file__).resolve().parent
@ -20,38 +33,42 @@ log_size = 512
debug = False 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 = loads(json_file.read()) output = loads(json_file.read())
json_file.close() json_file.close()
debug = output["debug"] debug = output["debug"]
except: except:
debug = debug debug = debug
# Check latest log size # Check latest log size
def checkSize(): def checkSize():
try: try:
if not path.isdir("logs"): if not path.isdir("logs"):
makedirs("logs", exist_ok=True) makedirs("logs", exist_ok=True)
log = stat("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("logs/latest.log", "rb") as f_in: with open("logs/latest.log", "rb") as f_in:
with gzipopen(f'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:
copyfileobj(f_in, f_out) copyfileobj(f_in, f_out)
print(f'Copied 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("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: str, guild: Union[Guild, None] = None, announce=True) -> None: def appendLog(message: str, guild: Union[Guild, None] = None, announce=True) -> None:
global debug global debug
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}'
else: else:
@ -59,25 +76,27 @@ def appendLog(message: str, guild: Union[Guild, None] = None, announce=True) ->
message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] [{guild} | {str(guild.id)}] {message}' message_formatted = f'[{datetime.now().strftime("%d.%m.%Y")}] [{datetime.now().strftime("%H:%M:%S")}] [{guild} | {str(guild.id)}] {message}'
else: else:
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, flush=True) print(message_formatted, flush=True)
checkSize() checkSize()
log = open('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: Any, filename: str) -> None: 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:
f.write(dumps(value, indent=4, ensure_ascii=False)) f.write(dumps(value, indent=4, ensure_ascii=False))
f.close() f.close()
def loadJson(filename: str) -> Any: 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 = loads(json_file.read()) output = loads(json_file.read())
json_file.close() json_file.close()
except Exception as exp: except Exception as exp:
@ -86,19 +105,26 @@ def loadJson(filename: str) -> Any:
output = {} output = {}
return output return output
def colorToStr(): def colorToStr():
pass pass
def strToColor(string: str) -> int: def strToColor(string: str) -> int:
return int(hex(int(string.replace("#", ""), 16)), 0) return int(hex(int(string.replace("#", ""), 16)), 0)
def gotCommand(message: Message) -> None: 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: Guild, variable: str) -> Any: def guildConfGet(guild: Guild, variable: str) -> Any:
global debug global debug
try: try:
@ -106,9 +132,12 @@ def guildConfGet(guild: Guild, variable: str) -> Any:
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: Guild, variable: str, value: Any) -> None: def guildConfSet(guild: Guild, variable: str, value: Any) -> None:
config = loadJson(f"guilds/{str(guild.id)}/config.json") config = loadJson(f"guilds/{str(guild.id)}/config.json")
config[variable] = value config[variable] = value
@ -120,6 +149,7 @@ def guildConfSet(guild: Guild, variable: str, value: Any) -> None:
saveJson(config, f"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: Guild, variable: str) -> None: def guildConfReset(guild: Guild, variable: str) -> None:
try: try:
config = loadJson(f"guilds/{str(guild.id)}/config.json") config = loadJson(f"guilds/{str(guild.id)}/config.json")
@ -134,6 +164,7 @@ def guildConfReset(guild: Guild, variable: str) -> None:
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: Guild) -> str: def guildLocaleGet(guild: Guild) -> str:
config = loadJson(f"config.json") config = loadJson(f"config.json")
try: try:
@ -145,21 +176,28 @@ def guildLocaleGet(guild: Guild) -> str:
else: else:
return locale return locale
def getMsg(string: str, guild: Union[Guild, None] = None) -> str: def getMsg(string: str, guild: Union[Guild, None] = None) -> str:
try: try:
locale = loadJson(f'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) -> Embed:
embed=Embed(title=title, description=description, color=color) def makeEmbed(title="", description="", footer="", color=0xFFFFFF) -> Embed:
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: int, guild: Guild, type: Literal["Any", "Text", "Voice"] = "Any") -> bool:
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
@ -180,6 +218,7 @@ def channelExists(number: int, guild: Guild, type: Literal["Any", "Text", "Voice
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: int, guild: Guild): def channelGetName(number: int, guild: Guild):
global debug global debug
try: try:
@ -190,6 +229,7 @@ def channelGetName(number: int, guild: Guild):
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: VoiceChannel) -> bool: def isUserVoice(vc: VoiceChannel) -> bool:
try: try:
channels_list = listdir(f"guilds/{str(vc.guild.id)}/channels/") channels_list = listdir(f"guilds/{str(vc.guild.id)}/channels/")
@ -199,68 +239,130 @@ def isUserVoice(vc: VoiceChannel) -> bool:
return False return False
except: except:
return False return False
async def removeUserVoice(vc: VoiceChannel) -> None: async def removeUserVoice(vc: VoiceChannel) -> None:
global debug global debug
channels_list = listdir(f"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"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 = utils.get(vc.guild.channels, id=vc.id) needed_channel = utils.get(vc.guild.channels, id=vc.id)
if loadJson("config.json")["enable_nomic"]: if loadJson("config.json")["enable_nomic"]:
nomic_channel = utils.get(vc.guild.channels, id=vc_conf["nomic"]) nomic_channel = utils.get(vc.guild.channels, id=vc_conf["nomic"])
remove(vc_file) remove(vc_file)
await needed_channel.delete() if needed_channel is None:
if debug: appendLog(
appendLog(f"Removed voice channel '{needed_channel}' ({str(needed_channel.id)}) of user with id {str(vc_conf['ownerid'])}", guild=vc.guild) f"Removed voice channel '{needed_channel}' 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) await needed_channel.delete()
if loadJson("config.json")["enable_nomic"]:
await nomic_channel.delete()
if debug: if debug:
appendLog(f"Removed nomic channel {nomic_channel} ({str(nomic_channel.id)}) of channel with id {str(needed_channel.id)}", 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 nomic channel '{nomic_channel}' of channel with id {str(needed_channel.id)}", guild=vc.guild) appendLog(
f"Removed voice channel '{needed_channel}' of user with id {str(vc_conf['ownerid'])}",
guild=vc.guild,
)
if loadJson("config.json")["enable_nomic"]:
if nomic_channel is None:
appendLog(
f"Removed nomic channel '{nomic_channel}' of channel with id {str(needed_channel.id)}",
guild=vc.guild,
)
else:
await nomic_channel.delete()
if debug:
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: VoiceChannel, category: CategoryChannel, member: Member) -> VoiceChannel:
async def createUserVoice(
vc: VoiceChannel, category: CategoryChannel, member: Member
) -> VoiceChannel:
global debug global debug
chan = {} chan = {}
overwrites_channel = { overwrites_channel = {
vc.guild.default_role: PermissionOverwrite(view_channel=True), vc.guild.default_role: PermissionOverwrite(view_channel=True),
vc.guild.me: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True), vc.guild.me: PermissionOverwrite(
member: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True) 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: PermissionOverwrite(view_channel=False, read_messages=False), vc.guild.default_role: PermissionOverwrite(
vc.guild.me: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True), view_channel=False, read_messages=False
member: PermissionOverwrite(read_messages=True, view_channel=True, manage_channels=True) ),
vc.guild.me: 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 path.isdir(f"guilds/{str(created_channel.guild.id)}/channels"): if not path.isdir(f"guilds/{str(created_channel.guild.id)}/channels"):
makedirs(f"guilds/{str(created_channel.guild.id)}/channels", exist_ok=True) makedirs(f"guilds/{str(created_channel.guild.id)}/channels", exist_ok=True)
vc_file = f"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)
if loadJson("config.json")["enable_nomic"]: if loadJson("config.json")["enable_nomic"]:
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))) 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 debug: if debug:
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}' ({str(nomic_channel.id)}) for channel '{created_channel}' ({str(created_channel.id)})",
guild=vc.guild,
)
else: else:
appendLog(f"Created nomic channel '{nomic_channel}' for channel '{created_channel}'", guild=vc.guild) appendLog(
f"Created nomic channel '{nomic_channel}' for channel '{created_channel}'",
guild=vc.guild,
)
chan["nomic"] = nomic_channel.id chan["nomic"] = nomic_channel.id
saveJson(chan, vc_file) saveJson(chan, vc_file)
return created_channel return created_channel
def isVoiceOfUser(vc: VoiceChannel, member: Member) -> bool: def isVoiceOfUser(vc: VoiceChannel, member: Member) -> bool:
vc_file = f"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)
@ -269,7 +371,10 @@ def isVoiceOfUser(vc: VoiceChannel, member: Member) -> bool:
else: else:
return False return False
async def changeNomicPerms(mode: Literal["deny", "allow"], vc: VoiceChannel, member: Member) -> None:
async def changeNomicPerms(
mode: Literal["deny", "allow"], vc: VoiceChannel, member: Member
) -> None:
vc_file = f"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)
if loadJson("config.json")["enable_nomic"]: if loadJson("config.json")["enable_nomic"]:
@ -279,6 +384,7 @@ async def changeNomicPerms(mode: Literal["deny", "allow"], vc: VoiceChannel, mem
else: else:
await nomic_channel.set_permissions(member, view_channel=True) await nomic_channel.set_permissions(member, view_channel=True)
async def clearTrash(client: Client): async def clearTrash(client: Client):
if not path.isdir(f"guilds/"): if not path.isdir(f"guilds/"):
makedirs(f"guilds", exist_ok=True) makedirs(f"guilds", exist_ok=True)
@ -290,8 +396,12 @@ async def clearTrash(client: Client):
for channel in channels_list: for channel in channels_list:
channel_id = channel[:-5] channel_id = channel[:-5]
try: try:
selected_channel = utils.get(guild_object.voice_channels, id=int(channel_id)) selected_channel = utils.get(
channel_owner = loadJson(f"guilds/{guild}/channels/{channel}")["ownerid"] guild_object.voice_channels, id=int(channel_id)
)
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:
@ -301,52 +411,82 @@ async def clearTrash(client: Client):
except: except:
remove(f"guilds/{guild}/channels/{channel_id}.json") remove(f"guilds/{guild}/channels/{channel_id}.json")
#async def autoClearTrash(client):
# execute clearTrash every 120 seconds # async def autoClearTrash(client):
# execute clearTrash every 120 seconds
def getHelpMessage(ctx: ApplicationContext, version: float) -> Embed: def getHelpMessage(ctx: ApplicationContext, version: float) -> Embed:
# channelExists(number, guild, type="Voice")
#channelExists(number, guild, type="Voice")
config = loadJson("config.json") config = loadJson("config.json")
if ctx.guild is not None: if ctx.guild is not None:
if channelExists(guildConfGet(ctx.guild, 'channel'), ctx.guild, type="Voice"): if channelExists(guildConfGet(ctx.guild, "channel"), ctx.guild, type="Voice"):
desc_channel = getMsg("help_channel_set", guild=ctx.guild).format(channelGetName(guildConfGet(ctx.guild, 'channel'), ctx.guild)) desc_channel = getMsg("help_channel_set", guild=ctx.guild).format(
channelGetName(guildConfGet(ctx.guild, "channel"), ctx.guild)
)
else: else:
desc_channel = getMsg("help_channel_none", guild=ctx.guild) desc_channel = getMsg("help_channel_none", guild=ctx.guild)
if channelExists(guildConfGet(ctx.guild, 'category'), ctx.guild, type="Any"): if channelExists(guildConfGet(ctx.guild, "category"), ctx.guild, type="Any"):
desc_category = getMsg("help_category_set", guild=ctx.guild).format(channelGetName(guildConfGet(ctx.guild, 'category'), ctx.guild)) desc_category = getMsg("help_category_set", guild=ctx.guild).format(
channelGetName(guildConfGet(ctx.guild, "category"), ctx.guild)
)
else: else:
desc_category = getMsg("help_category_none", guild=ctx.guild) desc_category = getMsg("help_category_none", guild=ctx.guild)
desc_locale = getMsg("help_locale", guild=ctx.guild).format(getMsg("locale_name", ctx.guild)) desc_locale = getMsg("help_locale", guild=ctx.guild).format(
getMsg("locale_name", ctx.guild)
)
description = "\n".join([desc_locale, desc_channel, desc_category]) description = "\n".join([desc_locale, desc_channel, desc_category])
embed=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=Embed(title=getMsg("help_title_dm", ctx.guild), color=strToColor(config["color_default"])) embed = Embed(
title=getMsg("help_title_dm", ctx.guild),
embed.set_author(name=f'{config["bot_name"]} v{str(version)}', url=config["bot_site"], icon_url=config["bot_icon"]) 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"],
)
if ctx.author.id == config["owner"]: if ctx.author.id == config["owner"]:
embed.add_field(name=f"/shutdown", value=getMsg("help_cmd_shutdown", ctx.guild), inline=False) embed.add_field(
name=f"/shutdown",
embed.add_field(name=f"/channel set", value=getMsg("help_cmd_channel", ctx.guild), inline=False) value=getMsg("help_cmd_shutdown", ctx.guild),
embed.add_field(name=f"/category set", value=getMsg("help_cmd_category", ctx.guild), inline=False) inline=False,
embed.add_field(name=f"/locale set", value=getMsg("help_cmd_locale", ctx.guild), inline=False) )
embed.add_field(
name=f"/channel set", value=getMsg("help_cmd_channel", ctx.guild), inline=False
)
embed.add_field(
name=f"/category set",
value=getMsg("help_cmd_category", ctx.guild),
inline=False,
)
embed.add_field(
name=f"/locale set", value=getMsg("help_cmd_locale", ctx.guild), inline=False
)
if ctx.guild is None: if ctx.guild is None:
embed.set_footer(text=getMsg("help_server", ctx.guild)) embed.set_footer(text=getMsg("help_server", ctx.guild))
else: else:
embed.set_footer(text=getMsg("help_notice_id", ctx.guild)) embed.set_footer(text=getMsg("help_notice_id", ctx.guild))
return embed return embed
async def guildConfigured(guild: Guild) -> str:
async def guildConfigured(guild: Guild) -> str:
output = {} output = {}
config = loadJson("config.json") config = loadJson("config.json")
@ -354,10 +494,16 @@ async def guildConfigured(guild: Guild) -> str:
if guildConfGet(guild, kind) is not None: if guildConfGet(guild, kind) is not None:
try: try:
guild_object = 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)
else: else:
output[kind] = getMsg("unconfigured_"+kind, guild) output[kind] = getMsg("unconfigured_" + kind, guild)
return getMsg("server_config", guild).format(getMsg("info_locale", guild).format(getMsg("locale_name", guild)), output["channel"], output["category"]) return getMsg("server_config", guild).format(
getMsg("info_locale", guild).format(getMsg("locale_name", guild)),
output["channel"],
output["category"],
)

View File

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

View File

@ -1,2 +1,2 @@
py-cord~=2.3.2 py-cord==2.5.0
requests~=2.28.2 requests==2.31.0

View File

@ -1,29 +1,43 @@
try: try:
from discord import Activity, ActivityType, ApplicationContext, Bot, Intents
from requests import get from requests import get
from discord import ApplicationContext, Intents, Bot, ActivityType, Activity, VoiceState
except Exception as exp: except Exception as exp:
print(f"Dependencies 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"
)
exit() exit()
from os import getpid from os import getpid, path
from shutil import rmtree
from functions import * from functions import *
pid = getpid() pid = getpid()
version = 1.8 version = 2.3
if loadJson("config.json")["owner"] == "SET-OWNER-ID" or loadJson("config.json")["bot_token"] == "SET-BOT-TOKEN": if (
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") not path.exists("config.json")
or 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 'config.json'\nLearn more here: https://git.end-play.xyz/profitroll/YusarinBot"
)
exit() exit()
if loadJson("config.json")["check_for_updates"]: if loadJson("config.json")["check_for_updates"]:
appendLog(f"Currently using YusarinBot v{str(version)}")
try: try:
serv_ver = loads(get("https://api.end-play.xyz/version?app=yusarinbot&apikey=publickey").text)["version"] releases = get(
if float(serv_ver) > version: "https://git.end-play.xyz/api/v1/repos/profitroll/YusarinBot/releases?draft=false&pre-release=false&page=1&limit=1"
appendLog(f"YusarinBot version {serv_ver} is available. Download new version here: https://git.end-play.xyz/profitroll/YusarinBot/releases/latest") ).json()
appendLog(f"Currently using YusarinBot v{str(version)}") if float(releases[0]["tag_name"].replace("v", "")) > version:
appendLog(
f"YusarinBot version {releases[0]['tag_name']} is available. Download new version here: {releases[0]['html_url']}"
)
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 = Bot(intents=intents) client = Bot(intents=intents)
@ -33,25 +47,29 @@ from cogs.cogChannel import CogChannel
from cogs.cogLocale import CogLocale from cogs.cogLocale import CogLocale
from cogs.cogUtility import CogUtility from cogs.cogUtility import CogUtility
@client.slash_command(name="help", description="Get information about this server") @client.slash_command(name="help", description="Get information about this server")
async def help(ctx: ApplicationContext): async def help(ctx: ApplicationContext):
await ctx.respond(embed=getHelpMessage(ctx, version)) await ctx.respond(embed=getHelpMessage(ctx, version))
@client.event @client.event
async def on_ready(): async def on_ready():
appendLog(f"Logged in as {client.user}") appendLog(f"Logged in as {client.user}")
config = loadJson("config.json") config = loadJson("config.json")
await client.change_presence(activity=Activity(type=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.add_cog(CogCategory(client)) client.add_cog(CogCategory(client))
client.add_cog(CogChannel(client)) client.add_cog(CogChannel(client))
client.add_cog(CogLocale(client)) client.add_cog(CogLocale(client))
client.add_cog(CogUtility(client)) client.add_cog(CogUtility(client))
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"])