From ae43a31e39210cd4605626a0916302649e43bd90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=A8=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D1=81=D0=BA=D0=B8=D0=B9?= Date: Thu, 22 Feb 2024 19:04:48 +0300 Subject: [PATCH] Initialize project --- .github/workflows/docker-push.yml | 44 ++++ LICENSE | 21 ++ README.md | 12 ++ cogs/auditChannel.py | 123 +++++++++++ cogs/automatization.py | 102 ++++++++++ cogs/buttons.py | 281 +++++++++++++++++++++++++ cogs/court.py | 47 +++++ cogs/customModals.py | 70 +++++++ cogs/modals.py | 166 +++++++++++++++ cogs/moderation.py | 328 ++++++++++++++++++++++++++++++ cogs/multiguild.py | 172 ++++++++++++++++ cogs/saveChannels.py | 36 ++++ cogs/ticket.py | 87 ++++++++ cogs/usersCommand.py | 157 ++++++++++++++ cogs/voiceBot.py | 207 +++++++++++++++++++ cogs/voiceChannels.py | 81 ++++++++ guilds/guilds.json | 6 + handlers.py | 91 +++++++++ localization/ru.json | 48 +++++ log.py | 31 +++ mute.json | 5 + requirements.txt | 17 ++ start.py | 4 + tempFiles/__init__.py | 1 + tempFiles/ticket.json | 5 + tempFiles/voiceName.json | 3 + tempFiles/voiceTempFile.json | 3 + tickets/tickets.json | 6 + users/users.json | 2 + 29 files changed, 2156 insertions(+) create mode 100644 .github/workflows/docker-push.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cogs/auditChannel.py create mode 100644 cogs/automatization.py create mode 100644 cogs/buttons.py create mode 100644 cogs/court.py create mode 100644 cogs/customModals.py create mode 100644 cogs/modals.py create mode 100644 cogs/moderation.py create mode 100644 cogs/multiguild.py create mode 100644 cogs/saveChannels.py create mode 100644 cogs/ticket.py create mode 100644 cogs/usersCommand.py create mode 100644 cogs/voiceBot.py create mode 100644 cogs/voiceChannels.py create mode 100644 guilds/guilds.json create mode 100644 handlers.py create mode 100644 localization/ru.json create mode 100644 log.py create mode 100644 mute.json create mode 100644 requirements.txt create mode 100644 start.py create mode 100644 tempFiles/__init__.py create mode 100644 tempFiles/ticket.json create mode 100644 tempFiles/voiceName.json create mode 100644 tempFiles/voiceTempFile.json create mode 100644 tickets/tickets.json create mode 100644 users/users.json diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml new file mode 100644 index 0000000..3cc1bf5 --- /dev/null +++ b/.github/workflows/docker-push.yml @@ -0,0 +1,44 @@ +name: Publish Docker image + +on: + release: + types: [published] + push: + branches: [ "master" ] + + pull_request: + branches: [ "master" ] + + +jobs: + push_to_registries: + name: Push Docker image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: | + ghcr.io/${{ github.repository }} + + - name: Build and push Docker images + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..04f9c87 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Dima YaFlay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf75966 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Aoyo +Aoyo is multifunctional discord bot, worked on disnake, supports localization and audit log(indev), 50% string objects is russian language + +# Bot can: + 1. Create ticket(and save then) + 2. Mute|Unmute user + 3. Create custom voice channel with setuping(using bot alright) + 4. Give report command to user (Right button on mouse on user or message) + 5. Save channel + 6. Localizations +# Support +If u know some languages(English, Spanish, etc.), u can help to localize this bot (create pull or issue) diff --git a/cogs/auditChannel.py b/cogs/auditChannel.py new file mode 100644 index 0000000..aef576d --- /dev/null +++ b/cogs/auditChannel.py @@ -0,0 +1,123 @@ +from disnake.ext import commands +from log import rootLogger +from handlers import * +import datetime +class audit(commands.Cog): + def __init__(self, bot): + self.bot = bot + rootLogger.info(f"{self.__class__.__name__} подключен") + + @bot.event + async def on_guild_channel_create(channel): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if "logs" in data["guilds"][f"{channel.guild.id}"]: + embed = disnake.Embed(title="Создание канала", description=f"Создан канал: {channel.name}! \n Категория канала: {channel.category.name}", timestamp=datetime.datetime.now()) + await bot.get_channel(data["guilds"][f"{channel.guild.id}"]["logs"]).send(embed=embed) + + @bot.event + async def on_guild_channel_update(before, after): + channel = before + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + after_changed = [] + before_changed = [] + if before.position != after.position: + after_changed.append(f"Position: {after.position}") + before_changed.append(f"Position: {before.position}") + if before.changed_roles != after.changed_roles: + after_changed.append(f'Roles: {after.changed_roles}') + before_changed.append(f'Roles: {before.changed_roles}') + if before.overwrites != after.overwrites: + for item in after.overwrites: + after_changed_perms = list(iter(after.overwrites[item].pair()[0])) + a_item_mention = item.mention + + for item in before.overwrites: + before_changed_perms = list(iter(before.overwrites[item].pair()[0])) + for item in range(len(before_changed_perms)): + + if after_changed_perms[item][1] != before_changed_perms[item][1]: + before_changed.append({a_item_mention: before_changed_perms[item]}) + after_changed.append({a_item_mention: after_changed_perms[item]}) + + if before.name != after.name: + after_changed.append(f'Name: {after.name}') + before_changed.append(f'Name: {before.name}') + after_changed = str(after_changed).replace("'", '').replace('{', '').replace('}', '').replace('[', '').replace(']', '') + before_changed = str(before_changed).replace("'", '').replace("{", '').replace("}", '').replace('[', '').replace(']', '') + print(after_changed) + if "logs" in data["guilds"][f"{channel.guild.id}"]: + embed = disnake.Embed(title="Создание канала", + description=f"Изменен канал: {channel.name}! \nКатегория канала: {channel.category.name};\nИзмененные настройки до: {before_changed};\nИзмененные настройки после: {after_changed};", + timestamp=datetime.datetime.now()) + await bot.get_channel(data["guilds"][f"{channel.guild.id}"]["logs"]).send(embed=embed) + + + @bot.event + async def on_guild_channel_delete(channel): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if "logs" in data["guilds"][f"{channel.guild.id}"]: + embed = disnake.Embed(title="Удаление канала", description=f"Удален канал: {channel.name}! \n Категория канала: {channel.category.name}", timestamp=datetime.datetime.now()) + await bot.get_channel(data["guilds"][f"{channel.guild.id}"]["logs"]).send(embed=embed) + + @bot.event + async def on_message_edit(before, after): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if "logs" in data["guilds"][f"{before.guild.id}"] and before.content != after.content: + embed = disnake.Embed(title="Изменение сообщения.", description=f"Автор: {before.author.mention}\n Канал: {before.channel.name}",timestamp=datetime.datetime.now(), color=0x00ff00) + embed.add_field(name="Сообщение до: ", value=before.content, inline=True) + embed.add_field(name="Сообщение после: ", value=after.content, inline=True) + embed.set_thumbnail(url=before.author.avatar) + await bot.get_channel(data["guilds"][f"{before.guild.id}"]["logs"]).send(embed=embed) + + @bot.event + async def on_message_delete(message): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if "logs" in data["guilds"][f"{message.guild.id}"]: + embed = disnake.Embed(title="Удаление сообщения.", description=f"Автор: {message.author.mention}\n Канал: {message.channel.name}\n Сообщение: {message.content}",timestamp=datetime.datetime.now(), color=disnake.Colour.from_rgb(186, 0, 6)) + embed.set_thumbnail(url=message.author.avatar) + await bot.get_channel(data["guilds"][f"{message.guild.id}"]["logs"]).send(embed=embed) + + @bot.event + async def on_member_update(before, after): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if "logs" in data["guilds"][f"{before.guild.id}"]: + if len(before.roles) > len(after.roles) or len(before.roles) < len(after.roles): + embed = disnake.Embed(title="Изменение ролей.", description=f"Пользователь: {before.mention}",timestamp=datetime.datetime.now(), color=0x00ff00) + rolesBefore = [] + rolesAfter = [] + for role in before.roles: + rolesBefore.append(role.mention) + for role in after.roles: + rolesAfter.append(role.mention) + embed.add_field(name="Роли до: ", value=str(rolesBefore).replace('[', '').replace(']', '').replace("'", ''), inline=True) + embed.add_field(name="Роли после: ", value=str(rolesAfter).replace('[', '').replace(']', '').replace("'", ''), inline=True) + embed.set_thumbnail(url=before.avatar) + if len(rolesAfter) > len(rolesBefore): + + for role in before.roles: + try: + rolesAfter.remove(role.mention) + except: + pass + + embed.add_field(name="Измененная роль:", value=str(rolesAfter).replace('[', '').replace(']', '').replace("'", '')) + else: + for role in after.roles: + try: + rolesBefore.remove(role.mention) + except: + pass + + embed.add_field(name="Измененная роль:", value=str(rolesBefore).replace('[', '').replace(']', '').replace("'", '')) + await bot.get_channel(data["guilds"][f"{before.guild.id}"]["logs"]).send(embed=embed) + + +def setup(bot): + bot.add_cog(audit(bot)) + \ No newline at end of file diff --git a/cogs/automatization.py b/cogs/automatization.py new file mode 100644 index 0000000..efa2051 --- /dev/null +++ b/cogs/automatization.py @@ -0,0 +1,102 @@ + +from handlers import * +from disnake.ext import commands, tasks +import disnake +from datetime import datetime +from log import rootLogger +import pyspw +import json +import aiogram +from aiogram import types +class automatization(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.mutes.start() + rootLogger.info("Starting automatization...") + + @tasks.loop(seconds=60.0) + async def mutes(self): + thisTime = datetime.now().strftime("%d/%m/%Y %H:%M") + path = Path("mute.json") + userData = json.loads(path.read_text(encoding="utf-8")) + if f"{thisTime}" in userData["mutes"]: + for items in userData["mutes"][f"{thisTime}"]: + guild = self.bot.get_guild(int(userData["mutes"][f"{thisTime}"][items]["guild"])) + member = await guild.fetch_member(int(userData["mutes"][f"{thisTime}"][items]["user"])) + await member.remove_roles(disnake.utils.get(guild.roles, id = int(takeSettings(guild.id, "mute_role")))) + del userData["mutes"][f"{thisTime}"] + path.write_text(json.dumps(userData, indent=4, ensure_ascii=False), encoding="utf-8", newline="\n") + + @bot.listen() + async def on_message(message: disnake.Message): + + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if "autoreaction" in data["guilds"][f"{message.guild.id}"] and not message.author.bot: + if f"{message.channel.id}" in data["guilds"][f"{message.guild.id}"]["autoreaction"]: + reaction1 = bot.get_emoji(int(data["guilds"][f"{message.guild.id}"]["autoreaction"][f"{message.channel.id}"]["reaction1"])) + await message.add_reaction(reaction1) + try: + reaction2 = bot.get_emoji(int(data["guilds"][f"{message.guild.id}"]["autoreaction"][f"{message.channel.id}"]["reaction2"])) + await message.add_reaction(reaction2) + except: pass + if "autobrench" in data["guilds"][f"{message.guild.id}"] and not message.author.bot: + if f"{message.channel.id}" in data["guilds"][f"{message.guild.id}"]["autobrench"]: + await message.create_thread(name='Autobranch') + + @bot.event + async def on_member_join(member): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + guildData = data["guilds"][f"{member.guild.id}"] + onJoinRole = disnake.utils.get(member.guild.roles, id = int(takeSettings(member.guild.id, "on_join_role"))) + if "guest_room" in guildData or guildData["server"] != ["None", {}]: + if "guest_room" in guildData: + channelGuestRoom = bot.get_channel(int(guildData["guest_room"])) + try: + description = guildData["guest_text"].format(user=member.mention) + except: + description = guildData["guest_text"] + emb = disnake.Embed(title=f"**Привет, {member.name}!**", description=description, colour=disnake.Colour.from_rgb(47, 49, 54)) + try: + emb.set_image(url=guildData["image_url"]) + except: + print() + + if guildData["server"] != ["None", {}]: + if guildData["server"]["server"] == "POOP": + try: + api = pyspw.SpApi(card_id=api_id, card_token=api_token).get_user(member.id).nickname + except: + print('Api error.... Again') + if api: + await member.edit(nick=api) + await member.add_roles(bot.get_guild(member.guild.id).get_role(int(guildData["server"]["role"]))) + if "guest_room" in guildData: + emb.set_footer(text=f"Игрок подтвержден! Ник: {api}") + else: + if "guest_room" in guildData: + emb.set_footer(text=f"Игрок не подтвержден!") + else: + await member.add_roles(onJoinRole) + if "guest_room" in guildData: + await channelGuestRoom.send(member.mention,embed=emb) + + + + + @bot.event + async def on_member_remove(member): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + guildData = data["guilds"][f"{member.guild.id}"] + if "image" in guildData: + channelGuestRoom = bot.get_channel(int(takeSettings(member.guild.id, "guest_room"))) + emb = disnake.Embed(title=f"**Пока-пока, {member.name}!**", colour=disnake.Colour.from_rgb(47, 49, 54)) + emb.set_image(url=guildData["image"]) + emb.set_footer(text="Ждем тебя снова!") + await channelGuestRoom.send(embed=emb) + + +def setup(bot): + bot.add_cog(automatization(bot)) \ No newline at end of file diff --git a/cogs/buttons.py b/cogs/buttons.py new file mode 100644 index 0000000..d94db20 --- /dev/null +++ b/cogs/buttons.py @@ -0,0 +1,281 @@ +from disnake.ext import commands +import disnake +import json +from pathlib import Path +import pyspw +from disnake import TextInputStyle +from handlers import api_id, api_token, takeSettings, bot +from asyncio import sleep +from random import randint +import datetime +import chat_exporter +import io + +class buttons(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @bot.listen("on_button_click") + async def button(inter: disnake.MessageInteraction): + if inter.component.custom_id in ['role1', 'role2', 'role3']: + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + role = bot.get_guild(inter.guild.id).get_role(data["guilds"][f"{inter.guild.id}"]['issues'][f"{inter.message.id}"][inter.component.custom_id]) + await inter.author.add_roles(role) + await inter.response.send_message(f'{role.mention} вам выдалась!', ephemeral=True) + elif inter.component.custom_id == "buttonOnModal": + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + modalData = data["guilds"][f"{inter.guild.id}"]["modals"][f"{inter.channel.id}"][f"{inter.message.id}"] + if data["guilds"][f"{inter.guild.id}"]["server"] != {}: + try: + api = pyspw.SpApi(card_id=api_id, card_token=api_token).get_user(inter.author.id).nickname + custom_id="customModal-api" + except Exception: + api = None + custom_id = "customModal" + + component=[ + disnake.ui.TextInput( + label=modalData["0"], + custom_id=modalData["0"], + style=TextInputStyle.short, + ), + ] + for i in range(len(modalData)-3): + i = i + 1 + modalDatas = modalData[f"{i}"] + component.append( + disnake.ui.TextInput( + label=f"{modalDatas}", + custom_id=f"{modalDatas}", + style=TextInputStyle.short, + ), + ) + + await inter.response.send_modal( + title="Aoyo Modal System", + custom_id=custom_id, + components=component,) + elif inter.component.custom_id in ["closePrivateRoom", "hidePrivateRoom"]: + guild = bot.get_guild(inter.guild.id) + role = guild.get_role(int(takeSettings(inter.guild.id, "on_join_role"))) + member = inter.author + if inter.component.custom_id == "closePrivateRoom": + overwrites = { + guild.default_role: disnake.PermissionOverwrite(connect=False), + role: disnake.PermissionOverwrite(connect=False), + member: disnake.PermissionOverwrite(connect=True) + } + else: + overwrites = { + guild.default_role: disnake.PermissionOverwrite(view_channel=False), + role: disnake.PermissionOverwrite(view_channel=False), + member: disnake.PermissionOverwrite(view_channel=True) + } + await bot.get_channel(inter.channel.id).edit(overwrites=overwrites) + await inter.response.send_message("Готово.", ephemeral=True) + + elif inter.component.custom_id in ["giveOwner", "kickUser", "deleteChannel"]: + path = Path("tempFiles/voiceTempFile.json") + data = json.loads(path.read_text(encoding="utf-8")) + if int(data["voice_channels"][f"{inter.channel.id}"]["channel_owner"]) == inter.author.id: + if inter.component.custom_id == "giveOwner": await inter.response.send_message("Отправьте пинг игрока, которому хотите передать права", ephemeral=True) + elif inter.component.custom_id == "deleteChannel": await inter.channel.delete() + else: await inter.response.send_message("Отправьте пинг игрока, которого хотите кикннуть", ephemeral=True) + def check(m): + return m.author == inter.author and m.channel == inter.channel + try: + message = await bot.wait_for('message', timeout=30.0, check=check) + except TimeoutError: + return + else: + content = message.mentions[0].id + await message.delete() + if inter.component.custom_id == "giveOwner": + data["voice_channels"][f"{inter.channel.id}"]["channel_owner"] = content + data["voice_channels"][f"{inter.channel.id}"]["old_channel_owner"] = inter.author.id + path.write_text(json.dumps(data, indent=6), encoding="utf-8", newline="\n") + await inter.response.send_message(f"Готово! Владелец канала теперь: <@{content}>", ephemeral=True) + else: + member = await bot.get_guild(inter.guild.id).fetch_member(int(content)) + await member.edit(voice_channel=None) + await inter.response.send_message(f"Готово! <@{content}> был кикнут", ephemeral=True) + else: + await inter.response.send_message("Вы не создатель канала!", ephemeral=True) + + elif inter.component.custom_id == "takeOwner": + path = Path("tempFiles/voiceTempFile.json") + data = json.loads(path.read_text(encoding="utf-8")) + if data["voice_channels"][f"{inter.channel.id}"]["old_channel_owner"] == inter.author.id: + data["voice_channels"][f"{inter.channel.id}"]["channel_owner"] = inter.author.id + path.write_text(json.dumps(data, indent=6), encoding="utf-8", newline="\n") + await inter.response.send_message(f"Готово! Владелец канала теперь: <@{inter.author.id}>", ephemeral=True) + elif inter.component.custom_id == "renamePrivateRoom": + await inter.response.send_modal( + title="Aoyo Modal System", + custom_id="renamePrivateRoom", + components=[ + disnake.ui.TextInput( + label='Новое название канала:', + placeholder="Я люблю нюхать бебру", + custom_id='newNamePrivateRoom', + style=disnake.TextInputStyle.short, + ) + ], + ) + elif inter.component.custom_id == "setUsersLimit": + await inter.response.send_modal( + title="Aoyo Modal System", + custom_id="setUsersLimit", + components=[ + disnake.ui.TextInput( + label='Новое максимальное кол-во пользователей:', + placeholder="1", + custom_id='newUserLimit', + style=disnake.TextInputStyle.short, + ) + ], + ) + # tickets + elif inter.component.custom_id in ["firstTicket", "secondTicket", "thirdTicket", "createTicket", "reportCreateTicket"]: + path = Path("guilds/guilds.json") + ticketData = Path("tempFiles/ticket.json") + aJsonData = json.loads(path.read_text(encoding="utf-8")) + fromJsonData = json.loads(path.read_text(encoding="utf-8")).get('guilds')[f"{inter.guild_id}"] + toJsonData = json.loads(ticketData.read_text(encoding="utf-8")) + mention = inter.author.mention + if inter.component.custom_id == "firstTicket": + role = fromJsonData["ticket"][f"{inter.channel.id}"][f"{inter.message.id}"]["first_role"] + if inter.component.custom_id == "secondTicket": + role = fromJsonData["ticket"][f"{inter.channel.id}"][f"{inter.message.id}"]["second_role"] + if inter.component.custom_id == "thirdTicket": + role = fromJsonData["ticket"][f"{inter.channel.id}"][f"{inter.message.id}"]["third_role"] + if inter.component.custom_id == "reportCreateTicket": + role = fromJsonData["moderation_role"] + embedMessage = inter.message.embeds[0] + mention = f"<@{embedMessage.footer.text}> \n{inter.author.mention}" + + await inter.message.edit(components=[ + disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id="reportAccept", label="Принять"), + disnake.ui.Button(label="Создать тикет", style=disnake.ButtonStyle.success, custom_id="reportCreateTicket", disabled=True), + disnake.ui.Button(label="Удалить репорт", style=disnake.ButtonStyle.danger, custom_id="deleteReport"), + ],) + embed = disnake.Embed(title="Управление тикетом", description="Если вы готовы завершить тикет, нажмите ниже.", colour=disnake.Color.blue()) + buttons = disnake.ui.View() + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id="closeTicket", label="Закрыть тикет")) + + guild = bot.get_guild(inter.guild.id) + roleOverwrite = guild.get_role(int(takeSettings(inter.guild.id, "on_join_role"))) + roleTicket = guild.get_role(int(role)) + overwrites = { + guild.default_role: disnake.PermissionOverwrite(view_channel=False), + roleOverwrite: disnake.PermissionOverwrite(view_channel=False), + inter.author: disnake.PermissionOverwrite(view_channel=True), + roleTicket: disnake.PermissionOverwrite(view_channel=True) + } + if not fromJsonData['ticket'].get('counter'): + aJsonData['guilds'][f'{inter.guild.id}']['ticket']['counter'] = 1 + aJsonData['guilds'][f'{inter.guild.id}']['ticket']['counter'] = int(aJsonData['guilds'][f'{inter.guild.id}']['ticket']['counter']) + 1 + count = aJsonData['guilds'][f'{inter.guild.id}']['ticket']['counter'] + category = disnake.utils.get(guild.categories, id=inter.channel.category.id) + channel = await guild.create_text_channel(name=f"ticket-{count}", category=category, overwrites=overwrites) + await channel.send(content=f"{mention}\n<@&{role}>",embed=embed, view=buttons) + newJsonData = { + "channel_owner": f"{inter.author.mention}", + "channel_name": f"{channel.name}", + "panel": f"{inter.component.custom_id}" + } + if not toJsonData["ticket"].get(f"{inter.guild_id}"): + toJsonData["ticket"][f"{inter.guild_id}"] = {} + + toJsonData["ticket"][f"{inter.guild_id}"][f"{channel.id}"] = newJsonData + + if inter.component.custom_id == "reportCreateTicket": + toJsonData["ticket"][f"{inter.guild_id}"][f"{channel.id}"]["user_name"] = f"{embedMessage.footer.text}" + path.write_text(json.dumps(aJsonData, indent=3), encoding="utf-8", newline="\n") + ticketData.write_text(json.dumps(toJsonData, indent=3), encoding="utf-8", newline="\n") + await inter.response.send_message(f'Ваш тикет: {channel.mention}', ephemeral=True) + + elif inter.component.custom_id == "closeTicket": + embed = disnake.Embed(title="Подтверждение", description=f"Вы уверены, что хотите закрыть тикет, {inter.author.name}?") + buttons = disnake.ui.View() + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id="closeTicketPerm", label="Да, закрыть тикет")) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id="cancelCloseTicket", label="Нет")) + await inter.response.send_message(embed=embed, view=buttons) + await inter.message.edit(components=[ + disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id="closeTicket", label="Закрыть тикет", disabled=True), + ],) + elif inter.component.custom_id == "cancelCloseTicket": + for message in (await inter.channel.history(limit=None, oldest_first=True).flatten()): + msg = await bot.get_channel(inter.channel.id).fetch_message(message.id) + break + await msg.edit(components=[ + disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id="closeTicket", label="Закрыть тикет", disabled=False), + ],) + await inter.message.delete() + elif inter.component.custom_id == "closeTicketPerm": + embed = disnake.Embed(title='**Aoyo ticket system**', description=f'Канал закрыт {inter.author.mention}', color=0x00ff00) + embed.set_image('https://media.discordapp.net/attachments/876280751488909332/979778066417070151/Frame_280.png?width=1440&height=4') + buttons = disnake.ui.View().add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id='deleteChannelTicket', label='Удалить канал')) + channel = bot.get_channel(inter.channel.id) + overwrites = { + inter.author: disnake.PermissionOverwrite(view_channel=False), + } + await inter.message.delete() + await channel.edit(overwrites=overwrites, name=f"closed-{channel.name[7:]}") + await channel.send(embed=embed, view=buttons) + + elif inter.component.custom_id == "deleteChannelTicket": + path = Path("guilds/guilds.json") + ticketData = Path("tempFiles/ticket.json") + fromJsonData = json.loads(path.read_text(encoding="utf-8")) + toJsonData = json.loads(ticketData.read_text(encoding="utf-8")) + guild = bot.get_guild(inter.guild.id) + member = await guild.fetch_member(int(toJsonData["ticket"][f"{inter.guild.id}"][f"{inter.channel.id}"]["channel_owner"].replace('<@', '').replace('>', ''))) + channel = bot.get_channel(int(fromJsonData["guilds"][f"{inter.guild_id}"]["transcript_channel"])) + transcript = await chat_exporter.export( + inter.channel, + bot=bot, + ) + embed = disnake.Embed(title='**Aoyo ticket system**', colour=disnake.Color.green()) + embed.add_field(name="Создатель", value=member.mention) + embed.add_field(name="Название канала", value=toJsonData["ticket"][f"{inter.guild.id}"][f"{inter.channel.id}"]["channel_name"], inline=False) + embed.add_field(name="Причина открытия", value=toJsonData["ticket"][f"{inter.guild.id}"][f"{inter.channel.id}"]["panel"], inline=True) + embed.set_image('https://media.discordapp.net/attachments/876280751488909332/979778066417070151/Frame_280.png?width=1440&height=4') + + transcript_file = disnake.File( + io.BytesIO(transcript.encode()), + filename=f"transcript-{inter.channel.name}.html", + ) + + await bot.get_channel(int(fromJsonData["guilds"][f"{inter.guild.id}"]["transcript_channel"])).send(f"**Сохранен и отправлен {inter.channel.name}**",file=transcript_file) + embed = disnake.Embed(title='**Aoyo ticket system**', description='Канал удалится в течении 10 секунд.', colour=disnake.Color.purple()) + await inter.message.edit(view=None) + await inter.response.send_message(embed=embed) + await sleep(9) + await bot.get_channel(inter.channel.id).delete(reason=f'Deleted by {inter.author.id}') + del toJsonData["ticket"][f"{inter.guild.id}"][f"{inter.channel.id}"] + ticketData.write_text(json.dumps(toJsonData, indent=3), encoding="utf-8", newline="\n") + # report + elif inter.component.custom_id == "reportAccept": + member = await bot.get_guild(inter.guild.id).fetch_member(int(inter.message.embeds[0].footer.text)) + await member.send(embed=disnake.Embed(title="Aoyo REPORT", description=f"Ваш репорт был принят и рассмотрен модератором {inter.author.mention}", color=0x00ff00,timestamp=datetime.datetime.now())) + await inter.message.edit(components=[ + disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id="reportAccept", label="Принять", disabled=True), + disnake.ui.Button(label="Создать тикет", style=disnake.ButtonStyle.success, custom_id="reportCreateTicket", disabled=True), + disnake.ui.Button(label="Удалить репорт", style=disnake.ButtonStyle.danger, custom_id="deleteReport", disabled=True), + ],) + await inter.message.edit(embed=inter.message.embeds[0].add_field(name="Репорт был принят!", value=f"Репорт принял {inter.author.mention}")) + await inter.response.send_message("Готово!", ephemeral=True) + elif inter.component.custom_id == "deleteReport": + await inter.response.send_message("Готово!", ephemeral=True) + await inter.message.edit(components=[ + disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id="reportAccept", label="Принять", disabled=True), + disnake.ui.Button(label="Создать тикет", style=disnake.ButtonStyle.success, custom_id="reportCreateTicket", disabled=True), + disnake.ui.Button(label="Удалить репорт", style=disnake.ButtonStyle.danger, custom_id="deleteReport", disabled=True), + ],) + else: + await inter.response.send_message('Ошибка!', ephemeral=True) +def setup(bot): + bot.add_cog(buttons(bot)) \ No newline at end of file diff --git a/cogs/court.py b/cogs/court.py new file mode 100644 index 0000000..9b4db54 --- /dev/null +++ b/cogs/court.py @@ -0,0 +1,47 @@ +from handlers import bot +from disnake.ext import commands +import disnake +class courtCogs(commands.Cog): + def __init__(self, bot: disnake.Client): + self.bot = bot + + + @commands.slash_command(name="court") + @commands.has_permissions(administrator=True) + async def court(self, inter): + embed = disnake.Embed(title="**Города, где проходил суд**") + embed.add_field(name=" Селестия", value="> **Расположение**: -1050 -500 \n > **Количество проведенных судов**: (3)", inline=False) + embed.add_field(name=" South Point", value="> **Расположение**: -444 727 \n > **Количество проведенных судов**: (3)", inline=False) + embed.set_footer(text="Города добавляются через кнопки ниже. За пренебрежение командами следует наказание!") + view = disnake.ui.View() + view.add_item(disnake.ui.Button(style=disnake.ButtonStyle.blurple, label="Добавить город", custom_id="addSity")) + view.add_item(disnake.ui.Button(style=disnake.ButtonStyle.blurple, label="Провести суд", custom_id="addCourt")) + + await inter.response.send_message(embed=embed, view=view) + + @commands.Cog.listener("on_button_click") + async def on_button_click_court(self, inter: disnake.MessageInteraction): + + if inter.component.custom_id == "addSity": + await inter.response.send_modal(title="Добавить город", custom_id="addSityModal", components=[ + disnake.ui.TextInput( + label="Название города", + custom_id="sityName", + style=disnake.TextInputStyle.short, + ), + disnake.ui.TextInput( + label="Координаты города", + custom_id="sityCoordinates", + style=disnake.TextInputStyle.short, + )]) + if inter.component.custom_id == "addCourt": + await inter.response.send_modal(title="Провести суд", custom_id="addCourtModal", components=[ + disnake.ui.TextInput( + label="Название города", + custom_id="sityName", + style=disnake.TextInputStyle.short, + )]) + + +def setup(bot): + bot.add_cog(courtCogs(bot)) \ No newline at end of file diff --git a/cogs/customModals.py b/cogs/customModals.py new file mode 100644 index 0000000..6d94e1c --- /dev/null +++ b/cogs/customModals.py @@ -0,0 +1,70 @@ +from handlers import * +from disnake.ext import * +from disnake import * +import disnake +from pathlib import Path +import pyspw +import json + +class customModals(commands.Cog): + def __init__(self, bot): + self.bot = bot + rootLogger.info(f"{__class__.__name__} подключен!") + + + @commands.slash_command( + name=Localised("createmodal", key="CREATE_MODAL_NAME"), + description=Localised("Create your`s own modal`s window.", key="CREATE_MODAL_DESC") + ) + @commands.has_permissions(administrator=True) + async def createCustomModals(inter, channel: disnake.TextChannel, text_in_message: str, first_label: str, second_label: str, third_label: str, fourth_label: str=Option("fourth_label"), fifth_label: str=Option("fifth_label")): + """Creation of your own windows (profiles) + + Args: + channel (disnake.TextChannel): This is required to transfer the channel where the message will go about the application + text_in_message (str): This requires to transmit the text that will be above the application button (questionnaire/modal) + first_label (str): Enter here what you want to see in the first line of the questionnaire (tests are welcome) + second_label (str): Enter here what you want to see in the second line of the questionnaire (tests are welcome) + third_label (str): Enter here what you want to see in the third line of the questionnaire (tests are welcome) + fourth_label (str, optional): Enter here what you want to see in the fourth line of the questionnaire (tests are welcome) + fifth_label (str, optional): Enter here what you want to see in the fifth line of the questionnaire (tests are welcome) + """ + embed = disnake.Embed(title="**Aoyo Modal SYSTEM**") + embed.add_field(name="Нажмите на кнопку нижу для подачи заявки!", value=f"{text_in_message}") + buttons = disnake.ui.View().add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id='buttonOnModal', label='Нажми на меня!')) + await inter.response.send_message("Готово!", ephemeral=True) + msg = await bot.get_channel(inter.channel.id).send(embed=embed, view=buttons) + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + arrayToSql = ["guild_id", "message_id", "channel_id", "first", "second", "third"] + dataToSql = [inter.guild.id, msg.id, channel.id, first_label, second_label, third_label] + newData = { + "channel": channel.id, + 0: first_label, + 1: second_label, + 2: third_label, + "message_id": msg.id + } + if type(fourth_label) == str: + newData["3"] = fourth_label + arrayToSql.append("fourth") + dataToSql.append(fourth_label) + if type(fifth_label) == str: + newData["4"] = fifth_label + arrayToSql.append("fifths") + dataToSql.append(fifth_label) + print(SQL().createTable("modals")) + print(SQL().insertData(arrayToSql, dataToSql, "modals")) + if f"modals" not in data["guilds"][f"{inter.guild.id}"]: + data["guilds"][f"{inter.guild.id}"]["modals"] = {} + if f"{inter.channel.id}" not in data["guilds"][f"{inter.guild.id}"]["modals"]: + data["guilds"][f"{inter.guild.id}"]["modals"][f"{inter.channel.id}"] = {} + + data["guilds"][f"{inter.guild.id}"]["modals"][f"{inter.channel.id}"][f"{msg.id}"] = newData + path.write_text(json.dumps(data, indent=6), encoding="utf-8", newline="\n") + + + + +def setup(bot): + bot.add_cog(customModals(bot)) \ No newline at end of file diff --git a/cogs/modals.py b/cogs/modals.py new file mode 100644 index 0000000..0ad2d3c --- /dev/null +++ b/cogs/modals.py @@ -0,0 +1,166 @@ +# modals.py +from handlers import logger, bot, api_id, api_token + +import disnake +from pathlib import Path +import json +from disnake.ext import commands +import pyspw +import sqlite3 +import psycopg2 +from handlers import execute +class modals(commands.Cog): + def __init__(self, bot: disnake.Client): + self.bot = bot + logger.info(f"Модуль {self.__class__.__name__} включен!") + + @commands.Cog.listener("on_modal_submit") + async def on_modal_submit(self, inter: disnake.ModalInteraction): + await inter.response.send_message(f"{inter.custom_id}", ephemeral=True) + if inter.custom_id in ["changeNamePrivateRoom", "newUsersLimit"]: + path = Path("tempFiles/voiceTempFile.json") + data = json.loads(path.read_text(encoding="utf-8")) + if ( + int(data["voice_channels"][f"{inter.channel.id}"]["channel_owner"]) + == inter.author.id + ): + path = Path("tempFiles/voiceName.json") + data = json.loads(path.read_text(encoding="utf-8")) + for key, value in inter.text_values.items(): + if inter.custom_id == "changeNamePrivateRoom": + data[f"{inter.author.id}"] = { + "chat_name": f"{value[:1024]}", + "channel_id": f"{inter.channel.id}", + } + path.write_text( + json.dumps(data, indent=7), encoding="utf-8", newline="\n" + ) + await self.bot.get_channel(inter.channel_id).edit(name=value[:1024]) + else: + await self.bot.get_channel(inter.channel_id).edit( + user_limit=int(value[:1024]) + ) + await inter.response.send_message("Готово!", ephemeral=True) + else: + await inter.response.send_message( + "Вы не создатель канала!", ephemeral=True + ) + elif inter.custom_id in ["customModal", "customModal-api", "interpol", "interpol-api"]: + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + embed = disnake.Embed( + description=f"{inter.author.mention}", colour=inter.author.colour + ) + for key, value in inter.text_values.items(): + embed.add_field( + name=key.capitalize(), + value=value[:1024], + inline=False, + ) + + embed.set_image(url=inter.author.avatar.url if inter.author.avatar else None) + if inter.custom_id in ["customModal-api", "interpol-api"]: + api = ( + pyspw.SpApi(card_id=api_id, card_token=api_token) + .get_user(inter.author.id) + .nickname + ) + embed.set_footer(text=f"Игрок подтвержден! Никнейм: {api}") + if inter.custom_id in ["interpol-api", "interpol"]: + embed.set_footer(text=f"{inter.channel.id}") + await self.bot.get_channel( + data["guilds"][f"{inter.guild.id}"]["modals"][f"{inter.channel.id}"]["channel"]).send( + f"{inter.author.mention}", + embed=embed, + components=[ + disnake.ui.Button( + label="Принять", + style=disnake.ButtonStyle.success, + custom_id="acceptInterpol", + ), + disnake.ui.Button( + label="Отклонить", + style=disnake.ButtonStyle.danger, + custom_id="declineInterpol", + ), + disnake.ui.Button( + label="Создать тикет", + style=disnake.ButtonStyle.blurple, + custom_id="createInterpolTicket", + ), + ], + ) + else: + await self.bot.get_channel( + data["guilds"][f"{inter.guild.id}"]["modals"][f"{inter.channel.id}"][ + f"{inter.message.id}" + ]["channel"] + ).send(f"{inter.author.mention}", embed=embed) + await inter.response.send_message("Готово!", ephemeral=True) + elif inter.custom_id in ["addCourtModal", "addSityModal"]: + modal_inter = inter + if inter.custom_id == "addCourtModal": + print("no") + sityName = "Unbound" + for key, values in modal_inter.text_values.items(): + sityName = values + i = -1 + embed = inter.message.embeds[0] + fieldFromEmbed = None + for field in embed.fields: + i = i + 1 + if field.name == sityName: + fieldFromEmbed = field + break + if not fieldFromEmbed: + await modal_inter.response.send_message("Вы указали неправильный город", ephemeral=True) + return False + symbol = (list(fieldFromEmbed.value)[-2]) + embed = inter.message.embeds[0] + embed.set_field_at(i, name=sityName, value=fieldFromEmbed.value.replace(f"({symbol})", f"({str(int(symbol)+1)})"), inline=False) + await modal_inter.response.send_message("Готово!", ephemeral=True) + await inter.message.edit(embed=embed) + else: + await modal_inter.response.send_message("Готово!", ephemeral=True) + sityName = "Unbound" + sityCoordinates = "Unbound" + for key, values in modal_inter.text_values.items(): + if key == "sityName": + sityName = values + elif key == "sityCoordinates": + sityCoordinates = values + embed = inter.message.embeds[0] + embed.add_field(name=sityName, value=f"> **Расположение**: {sityCoordinates} \n > **Количество проведенных судов**: (0)", inline=False) + await inter.message.edit(embed=embed) + elif inter.custom_id == "addFarmModal": + + + farmName = "Unbound" + farmCoordinates = "Unbound" + farmType = "Unbound" + sityName = "Unbound" + for key, values in inter.text_values.items(): + if key == "farmName": + farmName = values + elif key == "farmCoordinates": + farmCoordinates = values + elif key == "farmType": + farmType = values + elif key == "sityName": + sityName = values + if ";" in farmName or ";" in farmCoordinates or ";" in farmType or ";" in sityName: + await inter.response.send_message("SQL инъекции не работает, сын шалавы", ephemeral=True) + await (await inter.guild.fetch_member(945317832290336798)).send(f"Сын шлюхи {inter.author.mention} попытался использовать sql-инъекцию в {inter.channel.mention}") + return + if inter.message.embeds[0].title == "**Фермы**": + execute("INSERT INTO farms VALUES ({}, {}, {}, {}, {})".format(f'"{farmName}"', f'"{farmCoordinates}"', f'"{farmType}"', f'"{sityName}"', inter.author.id)) + else: + execute("INSERT INTO private_farms VALUES ({}, {}, {}, {}, {})".format(f'"{farmName}"', f'"{farmCoordinates}"', f'"{farmType}"', f'"{sityName}"', inter.author.id)) + + embed = inter.message.embeds[0] + embed.add_field(name=sityName, value=f"> **Название фермы**: {farmName} \n > **Расположение**: {farmCoordinates} \n > **Тип фермы**: {farmType}", inline=False) + await inter.message.edit(embed=embed) + await inter.response.send_message("Готово!", ephemeral=True) + +def setup(bot): + bot.add_cog(modals(bot)) diff --git a/cogs/moderation.py b/cogs/moderation.py new file mode 100644 index 0000000..997ef68 --- /dev/null +++ b/cogs/moderation.py @@ -0,0 +1,328 @@ +import asyncio +import datetime as dt +import json +import os +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Optional, Union + +import disnake +from disnake import Localized, OptionChoice +from disnake.ext import commands, tasks +from handlers import bot, now, takeSettings +from log import rootLogger + + +class common: + def __init__(self, + bot, + user_id: Optional[int] = None, + guild_id: Optional[int] = None): + """Common functions + + Args: + user_id (Optional[int], optional): _description_. Defaults to None. + guild_id (Optional[int], optional): _description_. Defaults to None. + """ + self.bot = bot + self.path = Path("mute.json") + self.guild_id = guild_id if guild_id is not None else None + self.user_id = user_id if user_id is not None else None + self.role = None + + async def embed_message(self, end_time: str, time: int, reason: str, muted: int, file: Optional[str] = None): + guild = bot.get_guild(self.guild_id) + member = await guild.fetch_member(muted) + channel = self.bot.get_channel(int(takeSettings(self.guild_id, "mute_channel"))) + emb = disnake.Embed(title=f"**System - Mute**", color=disnake.Color.purple(), timestamp=datetime.now()) + emb.add_field(name="**Выдал:**", value=f"<@{self.user_id}>", inline=True) + emb.add_field(name="**Нарушитель:**", value=f"<@{muted}>", inline=True) + emb.add_field(name="**Причина:**", value=reason, inline=False) + if file != None: + emb.set_image(file) + if end_time in ["м", "m"]: + emb.add_field(name="**Длительность:**", value="{} минут()".format(time)) + timesdelta = timedelta(minutes=float(time)) + elif end_time in ["ч", "h"]: + emb.add_field(name="**Длительность:**", value="{} час(ов)".format(time)) + timesdelta = timedelta(hours=float(time)) + elif end_time in ["д", "d"]: + emb.add_field(name="**Длительность:**", value="{} день(ей)".format(time)) + timesdelta = timedelta(days=float(time)) + + local_time = ((datetime.now() + timesdelta) + .astimezone() + .strftime("%d/%m/%Y %H:%M") + ) + path = Path("mute.json") + data = json.loads(path.read_text(encoding="utf-8")) + data["mutes"][f"{local_time}"] = {f"{member.id}": {"user": f"{member.id}", "guild": f"{self.guild_id}"}} + path.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + + await channel.send(embed=emb) + emb.remove_field(index=0) + emb.add_field(name="**Сервер:**", value=guild.name, inline=True) + await member.send(embed=emb) + + def checkIsInMuted(self): + """Проверка на наличие в базе мутов игрока + + Args: + user (int): айдишечка пользователя + + Returns: + bool: возвращается результат + """ + userData = json.loads(self.path.read_text(encoding="utf-8")) + for key in userData["mutes"]: + for data in userData["mutes"][key]: + + return int(self.user_id) == int(userData["mutes"][key][data]['user']) + + def new_DB(self): + """Create db + + Args: + guild_id (int): guild`s id for db + member (disnake.Member): member for data into db + """ + if not os.path.isfile(f"users/users_{self.guild_id}.json"): + with open(f"users/users_{self.guild_id}.json", "a") as file: + writeinfiledata = { + f"{self.guild_id}": { + f"{self.user_id}": { + "userID": f"{self.user_id}", + "warnCounter": "0", + "muteCounter": "1", + "userMention": f"<@{self.user_id}>", + "guild": f"{self.guild_id}", + } + } + } + json.dump(writeinfiledata, file) + file.close() + + path = Path(f"users/users_{self.guild_id}.json") + userData = json.loads(path.read_text(encoding="utf-8")) + if f"{self.user_id}" not in userData[f"{self.guild_id}"]: + newUserData = { + "userID": f"{self.user_id}", + "warnCounter": "0", + "muteCounter": "1", + "userMention": f"<@{self.user_id}>", + "guild": f"{self.guild_id}", + } + userData[f"{self.guild_id}"]["muteCounter"] = newUserData + else: + userData[f"{self.guild_id}"]["muteCounter"] = +1 + path.write_text(json.dumps(userData, indent=7), encoding="utf-8", newline="\n") + + def deleteMute(self): + """Удаление мута из базы данных + + Args: + user (int): Айди пользователя + """ + userData = json.loads(self.path.read_text(encoding="utf-8")) + for key in userData["mutes"]: + for data in userData["mutes"][key]: + if data == self.user_id: + del userData["mutes"][key] + self.path.write_text( + json.dumps(userData, indent=4, ensure_ascii=False), + encoding="utf-8", + newline="\n", + ) + return + + def mute(self): return int(takeSettings(self.guild_id, "mute_role")) + def moderator(self): return int(takeSettings(self.guild_id, "moderation_role")) + + async def issuedRole(self, types): + """Выдача роли в отдельной функции для выдачи роли + + Args: + guild_id (int): _description_ + user_id (int): _description_ + + """ + guild: disnake.Guild = self.bot.get_guild(int(self.guild_id)) + member: disnake.Member = await guild.fetch_member(int(self.user_id)) + + return bool(await member.add_roles(guild.get_role(types(self)))) + + + async def removeRole(self, type: str): + """Забирать роли для оптимизации кода + + Args: + guild_id (int): Передавать guild id для забирания роли + user_id (int): Передавать user id для забирания роли + type (str): Передавать вид роли для выполнения функции + + """ + guild: disnake.Guild = self.bot.get_guild(int(self.guild_id)) + if type == "mute": + role: disnake.Role = guild.get_role((takeSettings(self.guild.id, "mute_role"))) + member: disnake.Member = await guild.fetch_member(int(self.user_id)) + await member.remove_roles(role) + + +class moderationCommands(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.path = Path("mute.json") + rootLogger.info("Модуль {} Включен".format(self.__class__.__name__)) + + + async def preMuteFunc(self, user: int, guild: int): + """Передача данных для полной работы с всеми требуемыми функциями + + Args: + user (int): _description_ + guild (int): _description_ + + Returns: + _type_: _description_ + """ + + common(self.bot, user_id=user, guild_id=guild).new_DB() + if common(self.bot, user_id=user).checkIsInMuted(): + common(self.bot, user_id=user).deleteMute() + await common(self.bot, user_id=user, guild_id=guild).issuedRole(common.mute) + + + @commands.slash_command(name=Localized("clear", key="CLEAR_NAME"), description=Localized("Clear message in current channel", key="CLEAR_DESC")) + @commands.has_permissions(manage_messages=True) + async def clear(inter, amount: int): + await inter.response.send_message("Выполняется!", ephemeral=True) + channel = bot.get_channel(inter.channel.id) + await channel.purge(limit=amount + 1) + + + @commands.user_command(name=Localized("Mute", key="APP_MUTE_NAME") ) + @commands.has_permissions(manage_messages=True) + async def muteUserByApplication(self, inter: disnake.ApplicationCommandInteraction, member: disnake.Member): + await inter.response.send_modal( + title='Mute', + custom_id='muteByApp', + components=[ + disnake.ui.TextInput( + label="Время мута", + placeholder='2h', + custom_id='muteTime', + style=disnake.TextInputStyle.short, + max_length=3, + ), + disnake.ui.TextInput( + label="Причина", + placeholder='Флуд', + custom_id='muteReason', + style=disnake.TextInputStyle.short, + ), + ], + ) + try: + def check(m): + return m.author == inter.author and m.channel == inter.channel + modal_inter: disnake.ModalInteraction = await self.bot.wait_for( + event="modal_submit", + check=check, + timeout=30.0, + ) + await modal_inter.response.send_message('Готово!', ephemeral=True) + amount = modal_inter.text_values['muteTime'] + reason = modal_inter.text_values['muteReason'] + await self.preMuteFunc(member.id, inter.guild.id) + + await common(bot, inter.author.id, inter.guild.id).embed_message(end_time = amount[-1:], time = amount[:-1], reason = reason, muted=member.id) + except asyncio.TimeoutError: + return + + + + @commands.slash_command(name="embed", description=Localized("Create Embedded Message", key="CREATE_EMBED_DESC")) + @commands.has_permissions(administrator=True) + async def embed_message(self, inter, title: str, description: str, file: Optional[disnake.Attachment] = None): + emb = disnake.Embed(title=title, description=f"{description}", color=disnake.Color.purple()) + # emb.add_field(name=field_name, value=field_value, inline=False) + if file != None: emb.set_image(file.url) + await inter.response.send_message("Готово!", ephemeral=True) + await self.bot.get_channel(inter.channel.id).send(embed=emb) + + @commands.slash_command(name=Localized("mute", key="MUTE_NAME"), description=Localized("Mute user", key="MUTE_DESC")) + @commands.has_permissions(manage_messages=True) + async def add(self, inter, member: disnake.Member, amount: str, reason: str, file: disnake.Attachment): + await self.preMuteFunc(member.id, inter.guild.id) + await common(bot, inter.author.id, inter.guild.id).embed_message(end_time = amount[-1:], time = amount[:-1], reason = reason, muted=member.id, file= file.url) + await inter.response.send_message("Готово!", ephemeral=True) + + @commands.slash_command( + name=Localized("enableslowmode", key="ENABLEWLOWMODE_NAME"), description=Localized("Enable or disable slowmode in this channel", key="ENABLEWLOWMODE_DESC") + ) + @commands.has_permissions(kick_members=True) + async def enableslowmode(inter, amount: int, time: str = commands.Param( + choices=[ + OptionChoice("minute", "m"), + OptionChoice("second", "s"), + ] + ) + ): + + await inter.response.send_message("Готово!", ephemeral=True) + if time == "s": + await inter.channel.edit(slowmode_delay=amount) + elif time == "m": + await inter.channel.edit(slowmode_delay=(amount * 60)) + + else: + await inter.response.send_message( + "Неправильно указано время, или иное", ephemeral=True + ) + + + @commands.slash_command(name=Localized("unmute", key="UNMUTE_NAME"), description=Localized("Unmute user", key="UNMUTE_DESC")) + @commands.has_permissions(kick_members=True) + async def unmute(self, inter, user: disnake.Member): + role = self.bot.get_guild(inter.guild.id).get_role( + takeSettings(inter.guild.id, "mute_role") + ) + if role in user.roles: + await inter.response.send_message("Готово!", ephemeral=True) + await common(bot, user.id, inter.guild.id).removeRole("mute_role") + else: + await inter.response.send("Неправильный аргумент, или пользователь не замучен", ephemeral=True) + + @commands.slash_command(name=Localized("changenick", key='CHANGENICK_NAME'), description=Localized("Changes nickname ", key="CHANGENICK_DESC")) + async def changenick(self, inter, member: disnake.Member, changednick): + path= Path(f"users/users_{inter.guild.id}.json") + common(bot, member.id, inter.guild.id).new_DB() + await inter.response.send_message("Готово!", ephemeral=True) + readedData = json.loads(path.read_text(encoding="utf-8")) + warnCounter = int( + readedData[f"{inter.guild.id}"][f"{member.id}"]["warnCounter"] + ) + e = disnake.Embed( + description="Aoyo.Moderation SYSTEM", color=disnake.Color.purple() + ) + e.add_field(name="**Сменил:**", value=inter.author.mention) + e.add_field(name="**Старый ник:**", value=member.name) + e.add_field(name="**Новый ник:**", value=changednick) + e.add_field(name="**Количество смененных ников:**", value=f"{warnCounter+1}") + + readedData[f"{inter.guild_id}"][f"{member.id}"]["warnCounter"] = +1 + path.write_text( + json.dumps(readedData, indent=6), encoding="utf-8", newline="\n" + ) + + await member.edit(nick=changednick) + await self.bot.get_channel( + int(takeSettings(inter.guild_id, "mute_channel")) + ).send(embed=e) + e.remove_field(index=0) + e.add_field(name="**Сервер:**", value=inter.guild.name, inline=True) + await member.send(embed=e) + + +def setup(bot): + bot.add_cog(moderationCommands(bot)) diff --git a/cogs/multiguild.py b/cogs/multiguild.py new file mode 100644 index 0000000..b3de9dd --- /dev/null +++ b/cogs/multiguild.py @@ -0,0 +1,172 @@ +from disnake import * +from disnake.ext import commands +from handlers import * +from log import rootLogger +import json +from dislash import Option +from pathlib import Path +import disnake +from validators import url + + + +class multiGuildSettings(commands.Cog): + def __init__(self, bot): + self.bot = bot + rootLogger.info("Модуль {} подключен!".format(self.__class__.__name__)) + + + + + + @commands.slash_command(name=Localized('settings', key="SETTINGS")) + @commands.has_permissions(administrator=True) + async def settings(self, inter): + pass + + + @settings.sub_command( + name=Localized("setup", key="SETUP_NAME"), + description=Localized("Setting a bot on the server.", key="SETUP_DESC"),) + @commands.has_permissions(administrator=True) + async def allsettings(inter, + mute_role: disnake.Role, + mute_channel: disnake.TextChannel, + moderation_role: disnake.Role, + on_join_role: disnake.Role, + report_channel: disnake.TextChannel, + transcript_channel: disnake.TextChannel, + ): + writedData={ + "mute_role": f"{mute_role.id}", + "mute_channel": f"{mute_channel.id}", + "on_join_role": f"{on_join_role.id}", + "moderation_role": f"{moderation_role.id}", + "report_channel": f"{report_channel.id}", + } + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + + if f"{inter.guild.id}" not in data["guilds"]: + data["guilds"][f"{inter.guild.id}"] = {} + data["guilds"][f"{inter.guild.id}"]["mute_role"] = mute_role.id + data["guilds"][f"{inter.guild.id}"]["mute_channel"]= mute_channel.id + data["guilds"][f"{inter.guild.id}"]["on_join_role"] = on_join_role.id + data["guilds"][f"{inter.guild.id}"]["report_channel"] = report_channel.id + data["guilds"][f"{inter.guild.id}"]["moderation_role"] = moderation_role.id + data["guilds"][f"{inter.guild.id}"]["transcript_channel"] = transcript_channel.id + path.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + else: + data["guilds"][f"{inter.guild.id}"]["mute_role"] = mute_role.id + data["guilds"][f"{inter.guild.id}"]["mute_channel"]= mute_channel.id + data["guilds"][f"{inter.guild.id}"]["on_join_role"] = on_join_role.id + data["guilds"][f"{inter.guild.id}"]["report_channel"] = report_channel.id + data["guilds"][f"{inter.guild.id}"]["moderation_role"] = moderation_role.id + data["guilds"][f"{inter.guild.id}"]["transcript_channel"] = transcript_channel.id + path.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + await inter.response.send_message("Готово! Ваш сервер сохранен в настройках бота!", ephemeral=True) + + + @settings.sub_command(name=Localized('guestroom', key="GUESTROOM_NAME"), description=Localized('Settings of the Entrance of the player', key="GUESTROOM_DESC")) + @commands.has_permissions(administrator=True) + async def guestroom(inter, + guest_room: disnake.TextChannel=Option("guest_room",description="Укажите канал, в который будет отправляться сообщение о входе пользователя", required=True), + image: str=Option("image", description="Укажите ссылку на фото или гиф файл, который будет отправляться вместе с сообщением о прибытии!", required=True), + text: str=Option("text", description="Укажите текст, который должен присылаться вместе с сообщением о прибытии пользователя", required=True), + ): + if not url(image): + url = None + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + data["guilds"][f"{inter.guild.id}"]["guest_room"] = guest_room.id + data["guilds"][f"{inter.guild.id}"]["image_url"] = f"{image}" + data["guilds"][f"{inter.guild.id}"]["guest_text"] = f"{text}" + if "server" not in data: + data["guilds"][f"{inter.guild.id}"]["server"] = "None" + path.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + await inter.response.send_message("Готово!", ephemeral=True) + + + @settings.sub_command( + name=Localized('autobrench', key='AUTOBRENCH_NAME'), + description=Localized("Automatic creation of branches in the channels", key="AUTOBRENCH_DESC") + ) + @commands.has_permissions(administrator=True) + async def autobrench(inter: disnake.ApplicationCommandInteraction, channel: disnake.TextChannel): + path = Path("guilds/guilds.json") + jsonData = json.loads(path.read_text(encoding="utf-8")) + if f"autobrench" not in jsonData["guilds"][f"{inter.guild.id}"]: + jsonData["guilds"][f"{inter.guild.id}"]["autobrench"] = {} + path.write_text(json.dumps(jsonData, indent=4), encoding="utf-8", newline="\n") + jsonData = json.loads(path.read_text(encoding="utf-8")) + + jsonData["guilds"][f"{inter.guild.id}"]["autobrench"][f"{channel.id}"] = channel.id + path.write_text(json.dumps(jsonData, indent=4), encoding="utf-8", newline="\n") + await inter.response.send_message("Автоматическое создание веток настроено!", ephemeral=True) + + @settings.sub_command( + name=Localized("autoreaction", key="AUTOREACTION_NAME"), + description=Localized("Autoreaction setting up", key="AUTOREACTION_DESC") + ) + @commands.has_permissions(administrator=True) + async def autoreaction(inter: disnake.ApplicationCommandInteraction, channel: disnake.TextChannel, reaction1: disnake.Emoji, reaction2: disnake.Emoji=Option("reaction2")): + path = Path("guilds/guilds.json") + jsonData = json.loads(path.read_text(encoding="utf-8")) + if f"autoreaction" not in jsonData["guilds"][f"{inter.guild.id}"]: + jsonData["guilds"][f"{inter.guild.id}"]["autoreaction"] = {} + path.write_text(json.dumps(jsonData, indent=4), encoding="utf-8", newline="\n") + jsonData = json.loads(path.read_text(encoding="utf-8")) + try: + try: + second_react = reaction2.id + except: + second_react = "None" + jsonData["guilds"][f"{inter.guild.id}"]["autoreaction"][f"{channel.id}"] = { + "reaction1": reaction1.id, + "reaction2": second_react + } + path.write_text(json.dumps(jsonData, indent=4), encoding="utf-8", newline="\n") + await inter.response.send_message("Автореакция установлена", ephemeral=True) + except Exception as e: + await inter.response.send_message(f"Эмодзи не найдено. Попробуйте использовать эмодзи с этого сервера", ephemeral=True) + + + + @settings.sub_command( + name="auth", + description=Localized("User authentication, if it is in the server database", key="AUTH_DESC") + ) + + @commands.has_permissions(administrator=True) + async def authUser(inter, gived_role: disnake.Role, token_id: str, card_id: str): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + data["guilds"][f"{inter.guild.id}"]["server"] = { + "role": f"{gived_role.id}", + "token": token_id, + "card": card_id + } + path.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + await inter.response.send_message('Готово!', ephemeral=True) + + @settings.sub_command( + name=Localized("autovoice", key="AUTOVOICE_NAME"), + description="Automatic creation of voice channels (if the user connects to [+] create)" + ) + @commands.has_permissions(administrator=True) + async def autovoice(inter, answer: str = commands.Param(choices=[ + disnake.OptionChoice(Localized("Turn ON", key="AUTOVOICE_ON"), "on"), + disnake.OptionChoice(Localized("Turn OFF", key="AUTOVOICE_OFF"), "off"), + ] + )): + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if answer == "on": answer = True + else: answer = False + data["guilds"][f"{inter.guild.id}"]["autovoice"] = f"{answer}" + path.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + await inter.response.send_message('Готово!', ephemeral=True) + + +def setup(bot): + bot.add_cog(multiGuildSettings(bot)) diff --git a/cogs/saveChannels.py b/cogs/saveChannels.py new file mode 100644 index 0000000..e5e8f8f --- /dev/null +++ b/cogs/saveChannels.py @@ -0,0 +1,36 @@ +from disnake.ext import commands +import disnake +import chat_exporter +from typing import Optional +import io +class saveChannel(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.slash_command(name=disnake.Localized('save-channel', key='SAVECHANNEL_NAME'), + description=disnake.Localized('Saves channel data, and later sends them to a separate channel', key="SAVECHANNEL_DESC")) + @commands.has_permissions(administrator=True) + async def save_channel(self, inter, chat: Optional[disnake.TextChannel]): + if type(chat) == disnake.TextChannel: + channel = chat + else: + channel = inter.channel + + transcript = await chat_exporter.export( + channel, + bot=self.bot, + ) + if transcript is None: + return + + transcript_file = disnake.File( + io.BytesIO(transcript.encode()), + filename=f"transcript-{inter.channel.name}.html", + ) + + await inter.send(file=transcript_file) + await inter.response.send_message("Готово!", ephemeral=True) + +def setup(bot): + bot.add_cog(saveChannel(bot)) + \ No newline at end of file diff --git a/cogs/ticket.py b/cogs/ticket.py new file mode 100644 index 0000000..7bf4f92 --- /dev/null +++ b/cogs/ticket.py @@ -0,0 +1,87 @@ +from disnake.ext import commands +from disnake import * +from handlers import * +from log import * +import json +from dislash import Option +import disnake +from pathlib import Path + + +class ticket(commands.Cog): + def __init__(self,bot): + self.bot = bot + rootLogger.info(f"Модуль {self.__class__.__name__} подключен!") + + + + @commands.slash_command( + name=Localized("ticket", key="TICKET_NAME"), + description=Localized("Creating a tick for your server! (With buttons)", key="TICKET_DESC")) + @commands.has_permissions(administrator=True) + async def createTicket(inter, + ticket_name: str, + ticket_description: str, + channel: disnake.TextChannel, + first_button_name: str, + first_button_role: disnake.Role, + channel_for_transcript: disnake.TextChannel, + second_button_name: str=Option("second_button_name", description="Укажите название второй кнопки"), + second_button_role: disnake.Role=Option("second_button_name", description="Укажите роль для второй кнопки"), + third_button_name: str=Option("third_button_name", description="Укажите название третьей кнопки"), + third_button_role: disnake.Role = Option("third_button_name", type=disnake.Role, description="Укажите роль для третьей кнопки") ): + """ + Give several cookies to a user + + Parameters + ---------- + ticket_description: Description in create_ticket message + channel: Specify the channel through which the message will be sent to create tickets + first_button_name: First name of the button + first_button_role: The first role that will be pinged when the ticket is opened, + channel_for_transacript: The channel, where the message with transcript will be sent + """ + embed = disnake.Embed(title="**Ticket**", description=ticket_description, colour=disnake.Color.blue()) + embed.set_image('https://media.discordapp.net/attachments/876280751488909332/979778066417070151/Frame_280.png?width=1440&height=4') + embed.set_footer(text='Aoyo ticket system by YaFlay',) + buttons = disnake.ui.View() + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id="firstTicket", label=first_button_name)) + msg = await channel.send(embed=embed, view=buttons) + try: category = channel_for_transcript.category.id + except Exception: category = channel.category.id + + try: + sec_role = second_button_role.id + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id="secondTicket", label=second_button_name)) + except Exception: sec_role = "None" + + try: + third_role = third_button_role.id + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.green, custom_id="thirdTicket", label=third_button_name)) + except Exception: third_role = "None" + + writedData={ + "channel": f"{channel_for_transcript.id}", + "category": f"{category}", + "message_id": f"{msg.id}", + "guild": f"{inter.guild_id}", + "first_role": f"{first_button_role.id}", + "second_role": f"{sec_role}", + "third_role": f"{third_role}" + + } + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if f"{inter.guild.id}" not in data["guilds"]: + await inter.response.send_message("Вы не настроили сервер! сделайте это прямо сейчас при помощи команды /settings setup!") + return + elif "ticket" not in data["guilds"][f"{inter.guild_id}"]: + data["guilds"][f"{inter.guild_id}"]["ticket"] = {} + data["guilds"][f"{inter.guild_id}"]["ticket"][f"{channel.id}"] = {} + + data["guilds"][f"{inter.guild_id}"]["ticket"][f"{channel.id}"][f"{msg.id}"] = (writedData) + path.write_text(json.dumps(data, indent=3), encoding="utf-8", newline="\n") + await inter.response.send_message('Готово!', ephemeral=True) + +def setup(bot): + bot.add_cog(ticket(bot)) \ No newline at end of file diff --git a/cogs/usersCommand.py b/cogs/usersCommand.py new file mode 100644 index 0000000..761ebca --- /dev/null +++ b/cogs/usersCommand.py @@ -0,0 +1,157 @@ +from disnake.ext import commands +import disnake +from handlers import * +from log import * +import datetime +import json +import asyncio + + +class usersCommand(commands.Cog): + def __init__(self,bot): + self.bot = bot + rootLogger.info("Модуль {} подключен!".format(self.__class__.__name__)) + + @commands.user_command(name=disnake.Localized('Get avatar', key="GET_AVATAR")) + async def getAvatar(self, inter: disnake.ApplicationCommandInteraction, user: disnake.User): + embed = disnake.Embed(description=f"Аватарка пользователя {user.mention}") + embed.set_image(user.avatar.url) + await inter.response.send_message(inter.author.mention, embed=embed, ephemeral=True) + + @commands.message_command(name=disnake.Localized("Report", key="REPORT")) + async def moderationReport(self, inter: disnake.ApplicationCommandInteraction, message: disnake.Message): + await inter.response.send_modal( + title="Репорты", + custom_id="reportModal", + components=[ + disnake.ui.TextInput( + label="Комментарий к репорту", + placeholder='Меня оскорбили', + custom_id='comment', + style=disnake.TextInputStyle.short + + ) + ],) + try: + def check(m): + return m.author == inter.author and m.channel == inter.channel + modal_inter: disnake.ModalInteraction = await self.bot.wait_for( + event="modal_submit", + check=check, + timeout=30.0, + ) + valueFromFor = modal_inter.text_values['comment'] + embed = disnake.Embed( + title=f"Подали репорт!", + description=f"Ссылка на сообщение: [Тык](https://discord.com/channels/{message.guild.id}/{message.channel.id}/{message.id})\nАвтор сообщения: {message.author.name}\nАвтор репорта: {inter.author.name}\nСообщение, на которое подали жалобу: {message.content}\nПричина жалобы: {valueFromFor}", + color=0x00ff00, + timestamp=datetime.datetime.now() + ) + embed.set_image('https://media.discordapp.net/attachments/876280751488909332/979778066417070151/Frame_280.png?width=1440&height=4') + embed.set_thumbnail(url=message.author.avatar) + embed.set_footer(text=f"{inter.author.id}") + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + button = disnake.ui.View() + button.add_item(disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id="reportAccept", label="Принять")) + button.add_item(disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id="reportCreateTicket", label="Создать тикет")) + button.add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id="deleteReport", label="Удалить репорт")) + await self.bot.get_channel(int(data["guilds"][f"{message.guild.id}"]["report_channel"])).send(embed=embed, view=button) + await modal_inter.response.send_message("Вы удачно отправили репорт! Я отправил сообщение модерации.", ephemeral=True) + return + except asyncio.TimeoutError: + return + + @commands.message_command(name=disnake.Localized("Change Field", key="CHANGEFIELD")) + @commands.has_permissions(administrator=True) + async def change_field(self, inter: disnake.ApplicationCommandInteraction, message: disnake.Message): + await inter.response.send_modal( + title="Смена строки", + custom_id="changeField", + components=[ + disnake.ui.TextInput( + label="Номер строки(Если их одна, то напишите 0)", + placeholder='1', + custom_id='numberField', + style=disnake.TextInputStyle.short, + max_length=1, + + ), + disnake.ui.TextInput( + label="Новое значение", + placeholder='Меня зовут Кира Йошикаге', + custom_id='newField', + style=disnake.TextInputStyle.short, + ), + ], + ) + try: + def check(m): + return m.author == inter.author and m.channel == inter.channel + modal_inter: disnake.ModalInteraction = await self.bot.wait_for( + event="modal_submit", + check=check, + timeout=30.0, + ) + await modal_inter.response.send_message('Готово!', ephemeral=True) + numberOfField = int(modal_inter.text_values['numberField']) if int(modal_inter.text_values['numberField']) == 0 else int(modal_inter.text_values['numberField']) - 1 + print(numberOfField) + newField = modal_inter.text_values['newField'] + embed = message.embeds[0] + name = embed.fields[numberOfField].name + embed.remove_field(numberOfField) + embed.add_field(name=name, value=newField) + + await message.edit(embed=embed) + + except asyncio.TimeoutError: + return + + + @commands.slash_command(name=disnake.Localized('issue-role', key='ISSUE_ROLE_NAME'), description=disnake.Localized('Roaling by pressing the button', key="ISSUE_ROLE_DESC")) + @commands.has_permissions(administrator=True) + async def issueRole(self, inter, role1: disnake.Role, role2: disnake.Role, role3: disnake.Role): + + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding='utf-8')) + embed = disnake.Embed(title='Выдача ролей', description=f'{role1.mention}\n{role2.mention}\n{role3.mention}', color=0x00ff00) + embed.set_image('https://media.discordapp.net/attachments/876280751488909332/979778066417070151/Frame_280.png?width=1440&height=4') + channel = bot.get_channel(inter.channel.id) + message_id = await channel.send(embed=embed, + components=[ + disnake.ui.Button(label=role1.name, style=disnake.ButtonStyle.secondary, custom_id="role1"), + disnake.ui.Button(label=role2.name, style=disnake.ButtonStyle.secondary, custom_id="role2"), + disnake.ui.Button(label=role3.name, style=disnake.ButtonStyle.secondary, custom_id="role3"), + ], + ) + data["guilds"][f"{inter.guild.id}"]["issues"] = { + f"{message_id.id}":{ + "role1": role1.id, + "role2": role2.id, + "role3": role3.id, + }} + path.write_text(json.dumps(data, indent=7), encoding='utf-8', newline='\n') + await inter.response.send_message('Готово!', ephemeral=True) + + @commands.slash_command( + name='help', + description='Показать список команд') + async def help(inter): + embed = disnake.Embed(title="**Aoyo help**", color=0x00ff00) + embed.add_field(name="**!help**", value="Показать список команд", inline=False) + embed.add_field(name="!clear [кол-во]", value="Удаляет сообщения", inline=False) + embed.add_field(name="**/mute [пользователь] [время(1m, 1s)] [причина]**", value="Выдать мут", inline=False) + embed.add_field(name="**/unmute [пользователь]**", value="Размут пользователя", inline=False) + embed.add_field(name="**/changenick [пользователь] [ник]**", value="Изменить ник пользователя", inline=False) + embed.add_field(name="**/embed [название] [текст]**", value="Создать embed сообщение", inline=False) + embed.add_field(name="**/permmute [пользователь] [причина]**", value="Выдать мут навсегда", inline=False) + embed.add_field(name="**/settings setup [роль мута] [канал для мута] [модератор роль] [роль для нынезашедших] [канал для транскрипций тикета]**", value="Настройки сервера", inline=False) + embed.add_field(name="/ticket [название тикета] [описание тикета] [канал для тикета] [название первой кнопки] [пингующая роль первой кнопки] [и т.д.]", value="Создать тикет", inline=False) + embed.add_field(name="/settings autoreaction [канал] [реакция] [реакция]", value="Создать автореакцию", inline=False) + embed.add_field(name="/settings autobrench [канал]", value="Автосоздание веток в этом канале", inline=False) + embed.add_field(name="/deletechannel", value="Созвать собрание администрации для удаления канала", inline=False) + embed.add_field(name="/enableslowmode [время(1m, 1s)]", value="Включить медленный режим", inline=False) + + await inter.response.send_message(embed=embed, ephemeral=True) +def setup(bot): + bot.add_cog(usersCommand(bot)) diff --git a/cogs/voiceBot.py b/cogs/voiceBot.py new file mode 100644 index 0000000..d3a0e88 --- /dev/null +++ b/cogs/voiceBot.py @@ -0,0 +1,207 @@ +import disnake +import yt_dlp # type: ignore +from disnake.ext import commands, tasks +import asyncio +from typing import Any, Dict, Optional +# Suppress noise about console usage from errors +yt_dlp.utils.bug_reports_message = lambda: "" +from yandex_music import ClientAsync, Client +import ytmusicapi +from handlers import logger +from pathlib import Path +import json +import os + +type_to_name = { + 'track': 'трек', + 'artist': 'исполнитель', + 'album': 'альбом', + 'playlist': 'плейлист', + 'video': 'видео', + 'user': 'пользователь', + 'podcast': 'подкаст', + 'podcast_episode': 'эпизод подкаста', +} +yt = ytmusicapi.YTMusic() +ffmpeg_options = {'options': '-vn', 'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5'} + +ytdl = yt_dlp.YoutubeDL({'outtmpl': '%(id)s.%(ext)s'}) + +class YTDLSource(disnake.PCMVolumeTransformer): + def __init__(self, source: disnake.AudioSource, *, data: Dict[str, Any], volume: float = 0.125): + super().__init__(source, volume) + + self.title = data.get("title") + self.author = data.get("author") + self.source = data.get("source") + self.id = data.get("unicalID") + self.thumbnails: str = data.get("icon") + self.data = data + + @classmethod + async def from_url( + cls, url, *, loop: Optional[asyncio.AbstractEventLoop] = None, stream: bool = True + ): + player = None + if not "http" in url: + try: + url = f"https://youtube.com/watch?v={yt.search(url)[0]['videoId']}" + + except Exception: + env = os.environ + client = await ClientAsync(env.get("YANDEX_TOKEN")).init() + func = (await client.search(url, nocorrect=False)).best + artists = str(func.result.artists_name()).replace("'", '').replace("[", '').replace("]", '') + logger.info(f"[yandex] Downloading {func.result.title}") + # func.result.download(f"music.mp3") + logger.info(f"[yandex] Downloaded...") + logger.info(f"[yandex] Openning ....") + + try: + player = cls(disnake.FFmpegPCMAudio(await (await func.result.get_download_info_async())[0].get_direct_link_async(), **ffmpeg_options), data={'title': func.result.title, 'author': artists, 'source': "YandexMusic", 'fileName': f"{func.result.title}", 'type': "mp3", 'unicalID': f"https://music.yandex.ru/track/{func.result.id}", "icon": func.result.get_og_image_url()}) + except Exception: + player = cls(disnake.FFmpegPCMAudio(f"music.mp3", **ffmpeg_options), data={'title': func.result.title, 'author': artists, 'source': "YandexMusic", 'fileName': f"{func.result.title}", 'type': "mp3", 'unicalID': f"https://music.yandex.ru/track/{func.result.id}", "icon": func.result.get_og_image_url()}) + logger.info(f"[yandex] Playing {func.result.title}") + if not player: + loop = loop or asyncio.get_event_loop() + data: Any = await loop.run_in_executor( + None, lambda: ytdl.extract_info(url, download=False) + ) + filename = data["formats"][5]["url"] if stream else ytdl.prepare_filename(data) + data["author"] = data["channel"] + data["source"] = "YouTube" + data["type"] = data["formats"][5]["audio_ext"] + data["unicalID"] = url + data["icon"] = data["thumbnails"][38]["url"] + data["fileName"] = filename + title = data.get("title") + audio_type = data["type"] + logger.info(f"[YouTube] Playing {title}") + logger.info(f"[Youtube] Audio type {audio_type}") + player = cls(disnake.FFmpegPCMAudio(filename, **ffmpeg_options), data=data) + return player + + + + +class Music(commands.Cog): + def __init__(self, bot: commands.Bot): + self.bot = bot + self.queue = {"a": "a"} + + def __channel(self, inter) -> disnake.VoiceProtocol | None: + for voiceProtocol in self.bot.voice_clients: + if voiceProtocol.guild == inter.guild: + return voiceProtocol + # @tasks.loop() + # async def on_Voice_state_loop(self): + # voice_state = self.bot.voice_clients + + + @commands.slash_command() + async def join(self, inter: disnake.ApplicationCommandInteraction, channel: Optional[disnake.VoiceChannel] = None): + """Joins a voice channel""" + channel = inter.author.voice.channel if not channel else channel + if self.__channel(inter): await self.__channel(inter).disconnect() + await channel.connect() + await inter.response.send_message(f"Подключился к каналу {channel.mention}", ephemeral=True) + + # @commands.slash_command() + # async def queue(self, inter, *args): pass + + # @commands.slash_command(name="queue") + # async def add_to_queue(self, inter: disnake.ApplicationCommandInteraction, prompt_to_queue: str): + # try: + # await inter.response.defer() + # player = await YTDLSource.from_url(prompt_to_queue, loop=self.bot.loop, stream=True) + # try: + # if len(self.queue[inter.guild.id]) == 0: + # if self.__channel(inter): await self.__channel(inter).disconnect() + # self.start_playing(await self.ensure_voice(inter), player, inter.guild) + # await inter.send(f':mag_right: **Searching for** ``' + prompt_to_queue + '``\n<:youtube:763374159567781890> **Now Playing:** ``{}'.format(player.title) + "``") + # else: + # self.queue[len(self.queue[inter.guild.id])] = player + # await inter.send(f':mag_right: **Searching for** ``' + prompt_to_queue + '``\n<:youtube:763374159567781890> **Added to queue:** ``{}'.format(player.title) + "``") + # except KeyError: + # self.queue[inter.guild.id] = [] + # if self.__channel(inter): await self.__channel(inter).disconnect() + # self.start_playing(await self.ensure_voice(inter), player, inter.guild) + # await inter.send(f':mag_right: **Searching for** ``' + prompt_to_queue + '``\n<:youtube:763374159567781890> **Now Playing:** ``{}'.format(player.title) + "``") + + + # except Exception as e: + + # await inter.send(f"Somenthing went wrong - please try again later! ||{e}||") + + # def start_playing(self, voice_client, player, guild:disnake.Guild): + # self.queue[guild.id].append(player) + + + # for i in self.queue[guild.id]: + # try: + # voice_client.play(i, after=lambda e: print('Player error: %s' % e) if e else None) + + # except: + # pass + + + + @commands.slash_command() + async def play(self, inter: disnake.ApplicationCommandInteraction, prompt: str): + """Plays from a url (almost anything youtube_dl supports)""" + if self.__channel(inter): await self.__channel(inter).disconnect() + + voiceState: disnake.VoiceClient = await self.ensure_voice(inter) + + await inter.response.defer(with_message=True) + player = await YTDLSource.from_url(prompt, loop=self.bot.loop, stream=True) + embed = disnake.Embed(title="Вопроизведение музыки.", description=f"**Трек**: {player.title}\n **Автор**: {player.author}\n **Ссылка**: [Тык]({player.id})").set_footer(text=f"Источник: {player.source}").set_thumbnail(url=player.thumbnails) + await inter.send(embed=embed) + voiceState.play( + player, after=lambda e: logger.error(f"Error: {e}") if e else None + ) + + @commands.slash_command() + async def leave(self, inter: disnake.ApplicationCommandInteraction): + """Stops and disconnects the bot from voice""" + await self.__channel(inter).disconnect() + await inter.response.send_message("Бот вышел из канала", ephemeral=True) + + @commands.slash_command() + async def stop(self, inter: disnake.ApplicationCommandInteraction): + """Paused music""" + await self.ensure_voice(inter) + await inter.response.send_message("Бот остановил воспроизведение", ephemeral=True) + + @commands.slash_command() + async def pause(self, inter: disnake.ApplicationCommandInteraction): + """Paused music""" + await self.ensure_voice(inter, 'pause') + await inter.response.send_message("Бот остановил воспроизведение", ephemeral=True) + + @commands.slash_command() + async def resume(self, inter: disnake.ApplicationCommandInteraction): + """Paused music""" + await self.ensure_voice(inter, 'resume') + await inter.response.send_message("Бот продолжил воспроизведение воспроизведение", ephemeral=True) + + + async def ensure_voice(self, inter, types:str = None): + if self.__channel(inter) == None: + if inter.author.voice: + return await inter.author.voice.channel.connect() + else: + await inter.send("You are not connected to a voice channel.", ephemeral=True) + raise commands.CommandError("Author not connected to a voice channel.") + elif types == "pause" and self.__channel(inter).is_playing(): + self.__channel(inter).pause() + elif types == "resume" and not self.__channel(inter).is_playing(): + self.__channel(inter).resume() + elif self.__channel(inter).is_playing(): + self.__channel(inter).stop() + + + + +def setup(bot): + bot.add_cog(Music(bot)) diff --git a/cogs/voiceChannels.py b/cogs/voiceChannels.py new file mode 100644 index 0000000..3a674bb --- /dev/null +++ b/cogs/voiceChannels.py @@ -0,0 +1,81 @@ +from disnake.ext import * +import disnake +from pathlib import Path +from handlers import * +import json +from log import rootLogger + +voiceChannelData = Path("tempFiles/voiceTempFile.json") +voiceChannelName = Path("tempFiles/voiceName.json") +class audioChannelAutomatization(commands.Cog): + def __init__(self, bot): + self.bot = bot + rootLogger.info(f'Модуль {self.__class__.__name__} включен!') + + + + + @bot.event + async def on_voice_state_update(member, before, after): + if after.channel and after.channel != before.channel and after.channel.name in ['[+] СОЗДАТЬ', '[+]СОЗДАТЬ']: + guild = bot.get_guild(member.guild.id) + + category = disnake.utils.get(guild.categories, id=after.channel.category.id) + + data = json.loads(voiceChannelName.read_text(encoding="utf-8")) + + if f"{member.id}" in data: + channel = await guild.create_voice_channel(name=f'{data[f"{member.id}"]["chat_name"]}', category=category) + else: + channel = await guild.create_voice_channel(name=f'{member.name}`s voice channel', category=category) + await member.edit(voice_channel=channel) + data[f"{member.id}"]["channel_id"] = f"{channel.id}" + voiceChannelName.write_text(json.dumps(data, indent=7), "utf-8", newline='\n') + embeded = disnake.Embed(title="Пользователь присоединился к каналу", description=f"Пользователь: {member.mention}\n Канал: {after.channel.name}",timestamp=datetime.datetime.now(), color=0x00ff00) + embed = disnake.Embed(title = '**Управление приватными комнатами**', + description="""Вы можете изменить конфигурацию своей комнаты с помощью кнопок ниже. +**Переименовать приватную комнату:** ✏️ +**Задать лимит участников приватной комнаты:**👥 +**Закрыть/Открыть приватную комнату:**🔒 +**Скрыть/Открыть приватную комнату:**👀 +**Удалить канал(только для создателей канала):** \"Удалить канал\"""", colour=disnake.Colour.from_rgb(47, 49, 54)) + buttons = disnake.ui.View() + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id='renamePrivateRoom', emoji='✏')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id='setUsersLimit', emoji='👥')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id='closePrivateRoom', emoji=f'🔒')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id='hidePrivateRoom', emoji=f'👀')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.secondary, custom_id='kickUser', label='Кикнуть пользователя')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id='deleteChannel', label='Удалить канал')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.danger, custom_id='giveOwner', label='Передать права на канал')) + buttons.add_item(disnake.ui.Button(style=disnake.ButtonStyle.success, custom_id='takeOwner', label='Забрать права на канал')) + await bot.get_guild(member.guild.id).get_channel(channel.id).send(embed=embed, view=buttons) + writeData = { + 'chat_name': f'{after.channel.name}', + 'channel_owner': f'{member.id}', + 'chat_id': f'{after.channel.id}', + } + data = json.loads(voiceChannelData.read_text(encoding="utf-8")) + data["voice_channels"][f'{after.channel.id}'] = writeData + voiceChannelData.write_text(json.dumps(data, indent=7), encoding="utf-8", newline="\n") + + + elif before.channel and after.channel != before.channel: + embeded = disnake.Embed(title="Пользователь покинул канал.", description=f"Пользователь: {member.mention}\n Канал: {before.channel.name}",timestamp=datetime.datetime.now(), color=disnake.Colour.from_rgb(186, 0, 6)) + guild = bot.get_guild(member.guild.id) + data = json.loads(voiceChannelName.read_text(encoding="utf-8")) + for item in data: + if data[item]['channel_id'] == str(before.channel.id): + if before.channel.name == f'{member.name}`s voice channel' or before.channel.name == data[item]["chat_name"] and not len(before.channel.members): + try: + await before.channel.delete() + except Exception as e: + print(e) + else: pass + path = Path("guilds/guilds.json") + data = json.loads(path.read_text(encoding="utf-8")) + if data["guilds"][f"{member.guild.id}"]["logs"]: + embeded.set_thumbnail(url=member.avatar) + await bot.get_channel(data["guilds"][f"{member.guild.id}"]["logs"]).send(embed=embeded) + +def setup(bot): + bot.add_cog(audioChannelAutomatization(bot)) \ No newline at end of file diff --git a/guilds/guilds.json b/guilds/guilds.json new file mode 100644 index 0000000..d042585 --- /dev/null +++ b/guilds/guilds.json @@ -0,0 +1,6 @@ +{ + "guilds":{ + + } +} + diff --git a/handlers.py b/handlers.py new file mode 100644 index 0000000..b36acec --- /dev/null +++ b/handlers.py @@ -0,0 +1,91 @@ +import os, datetime, disnake, sys, json +from disnake.ext import commands +from asyncio import sleep +from log import rootLogger +from pathlib import Path +from os import environ +import psycopg2 + +def takeSettings(guild_id, data): + path = Path("guilds/guilds.json") + readedData = json.loads(path.read_text(encoding="utf-8")) + pon = readedData["guilds"][f"{guild_id}"][f"{data}"] + return pon + +def execute(command:str, isNeedToGiveData: bool = False): + with psycopg2.connect( + environ.get("PSQL_HOST"), + dbname=environ.get("PSQL_DBNAME"), + port=environ.get("PSQL_PORT", "5432"), + user=environ.get("PSQL_USER"), + password=environ.get("PSQL_PASSWORD") + ) as conn: + cur = conn.cursor() + cur.execute(command) + conn.commit() + conn.close() + if isNeedToGiveData: return cur.fetchone() + else: return + + +TOKEN = environ.get("TOKEN") +api_token = environ.get("API_TOKEN") +api_id = environ.get("API_ID") + + + +main_path = __file__.replace(os.path.basename(__file__), "") +bot = commands.Bot(command_prefix="/", intents=disnake.Intents().all()) +bot.remove_command("help") + +now = datetime.datetime.now().strftime("%d/%m/%Y %H:%M") + +@bot.listen() +async def on_ready(): + status = [disnake.Status.online, disnake.Status.idle, disnake.Status.dnd] + rootLogger.warning(f"{bot.user} is online and connected to Discord.") + disnakeGameStatus = disnake.Game(name="Only slash commands! Author github: github.com/yawaflua", type=4) + for filename in os.listdir(f"{main_path}/cogs"): + if filename.endswith(".py"): + namefile = filename.replace(".py", '') + bot.load_extension(f"cogs.{namefile}") + i = 0 + while True: + await bot.change_presence(status=status[i], activity=disnakeGameStatus) + if i >= 2: i = 0 + i = i+1 + + await sleep(30) + + +@bot.command(is_owner=True, pass_context=True, alias="reload") +async def reload(ctx): + for filename in os.listdir(f"{main_path}/cogs"): + if filename.endswith(".py"): + namefile = filename.replace(".py", '') + bot.reload_extension(f"cogs.{namefile}") + await ctx.message.delete() + +@bot.command( pass_context = True, alias="clear") +@commands.has_permissions(manage_messages=True) +async def clear( ctx, amount = 1 ): + + await ctx.channel.purge( limit = 1 ) + await ctx.channel.purge( limit = amount ) + +@bot.command(is_owner=True, alias='leave') +async def leave(ctx, arg): + print(arg) + await bot.get_guild(int(arg)).leave() + +if __name__ == "__main__": + try: + bot.run(TOKEN) + except ConnectionError as e: + rootLogger.error(f"Time: {now}, Internet exception: {e}. Closing the process....") + sys.exit() + except Exception as e: + rootLogger.error(f"Time: {now} Exception: {e}. Writing in file...") + with open("logs/last_error.log", "a") as file: + file.write(e) + file.close() diff --git a/localization/ru.json b/localization/ru.json new file mode 100644 index 0000000..f5d95b8 --- /dev/null +++ b/localization/ru.json @@ -0,0 +1,48 @@ +{ + + "CREATE_MODAL_NAME": "создание-заявок", + "CREATE_MODAL_DESC":"Создание собственных анкет(Модалов)", + + "CLEAR_NAME": "очистить", + "CLEAR_DESC": "Очищает сообщения в чате", + "CREATE_EMBED_DESC": "Создание Embed сообщения", + "MUTE_NAME": "мут", + "APP_MUTE_NAME": "Замутить", + "MUTE_DESC":"Замутить пользователя", + "ENABLEWLOWMODE_NAME": "включить-медленный-режим", + "ENABLEWLOWMODE_DESC": "Включает слоумод в канале", + "UNMUTE_NAME": "размут", + "UNMUTE_DESC": "Размутить пользователя", + "CHANGENICK_NAME": "сменить-ник", + "CHANGENICK_DESC": "Смена ника выбранному пользователю(после 3 раз мут)", + + "SETTINGS": "настройки", + "SETUP_NAME": "базовая-настройка", + "SETUP_DESC": "Базовая настройка бота для этого сервера.", + "GUESTROOM_NAME": "оповещение-о-входе", + "GUESTROOM_DESC": "Настройка оповещения о входе / выходе игрока на / с сервер(а)", + "AUTOBRENCH_NAME": "автоматическое-создание-веток", + "AUTOBRENCH_DESC": "Настройка автоматического создания веток в определенном канале", + "AUTOREACTION_NAME": "автореакции", + "AUTOREACTION_DESC": "Настройка автоматических реакций в канале", + "AUTH_DESC": "Аутентификация пользователя, если тот есть в базе данных сервера", + "AUTOVOICE_NAME": "приватки", + "AUTOVOICE_DESC": "Автоматическое создание голосовых каналов(Если пользователь подключается к [+] СОЗДАТЬ)", + "AUTOVOICE_ON": "Включить приватки", + "AUTOVOICE_OFF": "Выключить приватки", + + "SAVECHANNEL_NAME": "сохранить-канал", + "SAVECHANNEL_DESC": "Сохраняет канал и отправляет его как файл", + + "TICKET_NAME": "создание-тикетов", + "TICKET_DESC": "Создает сообщение для открытия тикетов(До 3 кнопок)", + + "GET_AVATAR": "Получить аватарку", + "REPORT": "Кинуть репорт", + "CHANGEFIELD": "Сменить Field", + "ISSUE_ROLE_NAME": "выдача-ролей", + "ISSUE_ROLE_DESC": "Выдача ролей по нажатию на кнопку" + + +} + diff --git a/log.py b/log.py new file mode 100644 index 0000000..5040d87 --- /dev/null +++ b/log.py @@ -0,0 +1,31 @@ +import logging +import os +import random + +main_path = __file__.replace(os.path.basename(__file__), '') + +formatter = "[%(asctime)s] %(message)s" +logFormatter = logging.Formatter(formatter) +logging.basicConfig(format=formatter) +logging.basicConfig(level=logging.INFO) +global rootLogger + + +fileHandler = logging.FileHandler("{0}/{1}.txt".format(f'{main_path}/logs', 'DISCORD_LOG')) +fileHandler.setFormatter(logFormatter) + +if os.path.getsize(f'{main_path}/logs/DISCORD_LOG.txt') >= 52428800: + fileHandler.close() + a = random.random() + os.rename(f'{main_path}/logs/DISCORD_LOG.txt', f'DISCORD_LOG_{a}.txt') + with open(f'{main_path}/logs/DISCORD_LOG.txt', 'w+') as file: + file.write('Cleaning up logs!') + file.close() + +logging.basicConfig(level=logging.WARNING) +consoleHandler = logging.StreamHandler() +consoleHandler.setFormatter(logFormatter) +rootLogger = logging.getLogger() +rootLogger.addHandler(fileHandler) +# rootLogger.addHandler(consoleHandler) + diff --git a/mute.json b/mute.json new file mode 100644 index 0000000..c99e4ad --- /dev/null +++ b/mute.json @@ -0,0 +1,5 @@ +{ + "mutes":[{ + + }] +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..00c94a4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,17 @@ +aiogram==2.25.1 +aiohttp==3.8.4 +chat_exporter==2.6.1 +config==0.5.1 +dislash.py==1.4.9 +disnake==2.9.0 +Django==4.2.3 +mojang==0.2.0 +Pillow==9.5.0 +Pillow==10.0.0 +psycopg2_binary==2.9.6 +Py_SPW==1.4.4 +Requests==2.31.0 +validators==0.20.0 +yandex_music==2.1.1 +yt_dlp==2023.3.4 +ytmusicapi==1.1.0 diff --git a/start.py b/start.py new file mode 100644 index 0000000..15248f6 --- /dev/null +++ b/start.py @@ -0,0 +1,4 @@ +from handlers import * +from config import TOKEN + + diff --git a/tempFiles/__init__.py b/tempFiles/__init__.py new file mode 100644 index 0000000..89abf9e --- /dev/null +++ b/tempFiles/__init__.py @@ -0,0 +1 @@ +# just ignore this diff --git a/tempFiles/ticket.json b/tempFiles/ticket.json new file mode 100644 index 0000000..8ba2dd8 --- /dev/null +++ b/tempFiles/ticket.json @@ -0,0 +1,5 @@ +{ + "ticket": { + + } +} \ No newline at end of file diff --git a/tempFiles/voiceName.json b/tempFiles/voiceName.json new file mode 100644 index 0000000..3e43281 --- /dev/null +++ b/tempFiles/voiceName.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tempFiles/voiceTempFile.json b/tempFiles/voiceTempFile.json new file mode 100644 index 0000000..3e43281 --- /dev/null +++ b/tempFiles/voiceTempFile.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tickets/tickets.json b/tickets/tickets.json new file mode 100644 index 0000000..b45a0e4 --- /dev/null +++ b/tickets/tickets.json @@ -0,0 +1,6 @@ +{ + "tickets":{ + + } + +} \ No newline at end of file diff --git a/users/users.json b/users/users.json new file mode 100644 index 0000000..49b9811 --- /dev/null +++ b/users/users.json @@ -0,0 +1,2 @@ +{} + \ No newline at end of file