9 Commits
test ... master

Author SHA1 Message Date
Slava
0946a7f51e Update .gitlab-ci.yml file 2024-05-28 21:24:14 +00:00
Slava
3e0a0c197a Update .gitlab-ci.yml file 2024-05-28 19:58:20 +00:00
Slava
b43c5cf5c1 Удалить check.py 2024-05-28 19:56:55 +00:00
bot
a136491362 restore 2024-05-28 22:55:47 +03:00
bacon
413736b44d changed logger 2024-04-13 20:03:57 +03:00
bot
78be10a88b v0.0.8 2024-03-25 10:14:57 +03:00
Slava
c3140403b5 Update .gitlab-ci.yml file 2024-03-16 17:51:18 +00:00
Slava
c60502a241 Update .gitlab-ci.yml file 2024-03-16 10:46:43 +00:00
Slava
17f1972104 Update .gitlab-ci.yml file 2024-03-16 10:43:16 +00:00
54 changed files with 997 additions and 1281 deletions

View File

@@ -1,11 +0,0 @@
/tmp/
audio/*/
/.idea
user.db
*.pyc
/.run/
.env
*.exe
/venv/
config.json
.editorconfig

11
.gitignore vendored
View File

@@ -1,11 +1,14 @@
/tmp/ /tmp/
audio/*/ /audio/*/
/.idea /.idea
user.db /user.db
*.pyc *.pyc
/.run/ /.run/
.env /.env
*.exe *.exe
/venv/ /venv/
/fun_and_admin_bot.egg-info/
/.YMcache/
config.json config.json
.editorconfig /env/
/.project

View File

@@ -1,136 +1,19 @@
# You can override the included template(s) by including variable overrides
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
stages: stages:
- 'build' - 'test'
- 'push' - 'code_quality'
- 'deploy'
variables:
# DOCKER_HOST: tcp://docker:2375
# fill those if you have a proxy in your environment
DOCKER_DRIVER: overlay2
# See https://github.com/docker-library/docker/pull/166
DOCKER_TLS_CERTDIR: "/certs"
make_image:
stage: build
image: docker:26.1.3
only:
# We want this job to be run on tags only.
refs:
- tags
- push
changes:
- ^README.md
- ^CHANGELOG
- ^.gitattribute
- ^.gitignore
- ^.gitlab-ci.yml
- src/**/*
- requirements.txt
services:
- docker:26.1.3-dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
# fetches the latest image (not failing if image is not found)
- docker pull $CI_REGISTRY_IMAGE:latest || true
# builds the project, passing proxy variables, using OCI labels
# notice the cache-from, which is going to use the image we just pulled locally
# the built image is tagged locally with the commit SHA, and then pushed to
# the GitLab registry
- >
DOCKER_BUILDKIT=1 docker build
--pull
--cache-from $CI_REGISTRY_IMAGE:latest
--label "org.opencontainers.image.title=$CI_PROJECT_TITLE"
--label "org.opencontainers.image.url=$CI_PROJECT_URL"
--label "org.opencontainers.image.created=$CI_JOB_STARTED_AT"
--label "org.opencontainers.image.revision=$CI_COMMIT_SHA"
--label "org.opencontainers.image.version=$CI_COMMIT_REF_NAME"
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
.
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# Here, the goal is to tag the "master" branch as "latest"
Push latest:
needs: ['make_image']
variables:
# We are just playing with Docker here.
# We do not need GitLab to clone the source code.
GIT_STRATEGY: none
stage: push
only:
changes:
- ^README.md
- ^CHANGELOG
- ^.gitattribute
- ^.gitignore
- ^.gitlab-ci.yml
- src/**/*
- requirements.txt
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
# Because we have no guarantee that this job will be picked up by the same runner
# that built the image in the previous step, we pull it again locally
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# Then we tag it "latest"
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:latest
# Annnd we push it.
- docker push $CI_REGISTRY_IMAGE:latest
# Finally, the goal here is to Docker tag any Git tag
# GitLab will start a new pipeline everytime a Git tag is created, which is pretty awesome
Push commit:
needs: ['make_image']
variables:
# Again, we do not need the source code here. Just playing with Docker.
GIT_STRATEGY: none
stage: push
only:
# We want this job to be run on tags only.
refs:
- ^tags
- push
changes:
- ^README.md
- ^CHANGELOG
- ^.gitattribute
- ^.gitignore
- ^.gitlab-ci.yml
- src/**/*
- requirements.txt
before_script: sast:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY stage: 'test'
script: include:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - template: Security/SAST.gitlab-ci.yml
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA - template: Security/Dependency-Scanning.gitlab-ci.yml
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME - template: Security/SAST-IaC.gitlab-ci.yml
Push tag:
needs: ['make_image']
variables:
# Again, we do not need the source code here. Just playing with Docker.
GIT_STRATEGY: none
stage: push
only:
# We want this job to be run on tags only.
refs:
- tags
changes:
- ^README.md
- ^CHANGELOG
- ^.gitattribute
- ^.gitignore
- ^.gitlab-ci.yml
- src/**/*
- requirements.txt
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG

2
CHANGELOG Normal file
View File

@@ -0,0 +1,2 @@
0.0.5
Initial

View File

@@ -1,22 +0,0 @@
FROM python:3.12.2-slim-bookworm as builder
LABEL authors="bacon"
WORKDIR /app
COPY requirements.txt /app
RUN pip wheel install \
--no-cache-dir -q \
--no-deps --wheel-dir /app/wheels \
-r requirements.txt
FROM python:3.12.2-slim-bookworm as runner
WORKDIR /app
COPY --from=builder /app/wheels /wheels
RUN apt-get update -qq && \
apt-get install -qq --no-install-recommends ffmpeg -y && \
apt-get clean -qq autoclean && \
pip install --no-cache -q /wheels/*
COPY src/ /app
ENTRYPOINT ["python", "bot.py"]
#ENTRYPOINT ["ffmpeg"]

View File

@@ -1,5 +1,5 @@
__version__ = '0.0.7' __version__ = '0.0.8'
__title__ = "src" __title__ = "Pisya_bot"
__author__ = "baconborn" __author__ = "baconborn"
from typing import NamedTuple, Literal from typing import NamedTuple, Literal
@@ -13,4 +13,4 @@ class VersionInfo(NamedTuple):
serial: int serial: int
version_info: VersionInfo = VersionInfo(major=0, minor=0, micro=7, releaselevel="alpha", serial=0) version_info: VersionInfo = VersionInfo(major=0, minor=0, micro=8, releaselevel="alpha", serial=0)

15
src/bot.py → bot.py Normal file → Executable file
View File

@@ -1,14 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio
from os import getenv from os import getenv
from os.path import isfile from os.path import isfile
from disnake import OptionType, Option, Localized, ApplicationCommandInteraction, Intents, __version__ from disnake import OptionType, Option, Localized, ApplicationCommandInteraction, Intents, __version__
from disnake.ext.commands import Bot, is_owner from disnake.ext.commands import Bot, is_owner
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger
from integral_lib.CogsPrep import work_with_cogs, cog_list from __init__ import __version__ as ver
from integral_lib.Comands import determine_prefix from lib.CogsPrep import work_with_cogs, cog_list
from integral_lib.Logger import logger from lib.Comands import determine_prefix
load_dotenv() load_dotenv()
if not isfile('.env') or not getenv('CONF_FILE'): if not isfile('.env') or not getenv('CONF_FILE'):
@@ -35,7 +37,7 @@ bot = Bot(command_prefix=determine_prefix,
bot.i18n.load("locale/") bot.i18n.load("locale/")
work_with_cogs('load', bot, cog_list()) asyncio.run(work_with_cogs('load', bot, asyncio.run(cog_list())))
@bot.event @bot.event
@@ -43,6 +45,7 @@ async def on_ready():
logger.info('Bot started') logger.info('Bot started')
logger.info(f'Disnake version {__version__}') logger.info(f'Disnake version {__version__}')
logger.info(f'We have logged in as {bot.user}') logger.info(f'We have logged in as {bot.user}')
logger.info(f'Version of bot is - v{ver}')
@bot.slash_command( @bot.slash_command(
@@ -138,14 +141,14 @@ async def reload(inter: ApplicationCommandInteraction, cog: str):
@reload.autocomplete('cog') @reload.autocomplete('cog')
async def _cog_opt(inter: ApplicationCommandInteraction, current: str): async def _cog_opt(inter: ApplicationCommandInteraction, current: str):
current = current.lower() current = current.lower()
_list = cog_list(fold='./cogs/') _list = await cog_list(fold='./cogs/')
return [choice for choice in _list if current in choice.lower()] return [choice for choice in _list if current in choice.lower()]
@enable.autocomplete('cog') @enable.autocomplete('cog')
async def _cog_opt(inter: ApplicationCommandInteraction, current: str): async def _cog_opt(inter: ApplicationCommandInteraction, current: str):
current = current.lower() current = current.lower()
_list = cog_list(fold='./cogs/disabled/') _list = await cog_list(fold='./cogs/disabled/')
return [choice for choice in _list if current in choice.lower()] return [choice for choice in _list if current in choice.lower()]

View File

@@ -2,16 +2,16 @@ from asyncio import sleep
import disnake import disnake
from disnake import Option, OptionType, Localized from disnake import Option, OptionType, Localized
from disnake.ext import tasks from disnake.ext import commands, tasks
from loguru import logger
from integral_lib.Comands import * from lib.Comands import read_json, write_json
from integral_lib.DB_Worker import fill_bd, prepare_db, work_with_db from lib.DB_Worker import fill_bd, prepare_db, work_with_db
from integral_lib.Logger import logger
class Admin(commands.Cog, name='Admin'): class Admin(commands.Cog, name='Admin'):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot # a defining src as global var in class self.bot = bot # a defining bot as global var in class
@commands.Cog.listener() # this is a decorator for events/listeners @commands.Cog.listener() # this is a decorator for events/listeners
async def on_ready(self): async def on_ready(self):
@@ -54,9 +54,11 @@ class Admin(commands.Cog, name='Admin'):
async def on_member_join(self, member): async def on_member_join(self, member):
await fill_bd(member.name, member.id, member.bot, member.nick, member.guild.id) await fill_bd(member.name, member.id, member.bot, member.nick, member.guild.id)
bot_role = read_json(member.guild.id, 'bot_role') # Get src role bot_role = read_json(member.guild.id, 'bot_role') # Get bot role
guest_role = read_json(member.guild.id, 'guest_role') # Get guest role guest_role = read_json(member.guild.id, 'guest_role') # Get guest role
logger.info(f"Bot role: {bot_role} | Guest role: {guest_role}")
logger.info(f'type bot_role: {type(bot_role)} | type guest_role: {type(guest_role)}')
if bot_role or guest_role: if bot_role or guest_role:
if member.bot == 0: if member.bot == 0:
role = disnake.utils.get(member.guild.roles, id=guest_role) role = disnake.utils.get(member.guild.roles, id=guest_role)
@@ -67,7 +69,7 @@ class Admin(commands.Cog, name='Admin'):
@commands.slash_command( @commands.slash_command(
name="set_default_role", name="set_default_role",
description=Localized("Set Default src role", key="DEF_ROLE"), description=Localized("Set Default bot role", key="DEF_ROLE"),
options=[ options=[
Option("role", Option("role",
"Specify role", "Specify role",
@@ -77,8 +79,9 @@ class Admin(commands.Cog, name='Admin'):
) )
@commands.has_permissions(administrator=True) @commands.has_permissions(administrator=True)
async def set_guest_role(self, inter: disnake.ApplicationCommandInteraction, role): async def set_guest_role(self, inter: disnake.ApplicationCommandInteraction, role):
print(type(role.id))
await write_json(inter.guild.id, "guest_role", role.id) await write_json(inter.guild.id, "guest_role", role.id)
await inter.response.send_message(f"Set up src role to: `{role.name}`", ephemeral=True) await inter.response.send_message(f"Set up bot role to: `{role.name}`", ephemeral=True)
@commands.command(name="set_prefix") @commands.command(name="set_prefix")
@commands.has_permissions(administrator=True) @commands.has_permissions(administrator=True)
@@ -89,7 +92,7 @@ class Admin(commands.Cog, name='Admin'):
@commands.guild_only() @commands.guild_only()
@commands.slash_command( @commands.slash_command(
name="set_prefix", name="set_prefix",
description="Setting up src prefix", description="Setting up bot prefix",
options=[ options=[
Option("prefix", Option("prefix",
"Specify prefix", "Specify prefix",
@@ -106,7 +109,7 @@ class Admin(commands.Cog, name='Admin'):
@commands.has_permissions(administrator=True) @commands.has_permissions(administrator=True)
@commands.slash_command( @commands.slash_command(
name="set_trigger_role", name="set_trigger_role",
description=Localized("Setting up role to trigger src", key="KEY_ROLE"), description=Localized("Setting up role to trigger bot", key="KEY_ROLE"),
options=[ options=[
Option("role", Option("role",
Localized("Specify role", key="SETUP_ROLE"), Localized("Specify role", key="SETUP_ROLE"),
@@ -120,7 +123,7 @@ class Admin(commands.Cog, name='Admin'):
@commands.slash_command( @commands.slash_command(
name="set_bot_role", name="set_bot_role",
description=Localized("Set src role", key="BOT_ROLE"), description=Localized("Set bot role", key="BOT_ROLE"),
options=[ options=[
Option("role", Option("role",
Localized("Specify role", key="SETUP_ROLE"), Localized("Specify role", key="SETUP_ROLE"),
@@ -134,7 +137,7 @@ class Admin(commands.Cog, name='Admin'):
await write_json(ctx.guild.id, await write_json(ctx.guild.id,
"bot_role", "bot_role",
role.id) role.id)
await ctx.send(f"Set up src role to: `{role.name}`", ephemeral=True) await ctx.send(f"Set up bot role to: `{role.name}`", ephemeral=True)
@set_bot_role.error @set_bot_role.error
@set_trigger_role.error @set_trigger_role.error

View File

@@ -1,12 +1,13 @@
import random import random
import disnake import disnake
from disnake import OptionChoice, Option, OptionType, Member, VoiceState, ApplicationCommandInteraction from disnake import OptionChoice, Option, OptionType, Member, VoiceState
from disnake.ext import commands from disnake.ext import commands
from disnake.utils import get
from loguru import logger
from integral_lib.ListGenerator import ListGenerator from lib.ListGenerator import ListGenerator
from integral_lib.Logger import logger from lib.Player import play_audio
from integral_lib.Player import play_audio
class Audio(commands.Cog, name='Audio'): class Audio(commands.Cog, name='Audio'):
@@ -27,15 +28,15 @@ class Audio(commands.Cog, name='Audio'):
logger.info('Skip playing by Game') logger.info('Skip playing by Game')
else: else:
# Prepare list of audio # Prepare list of audio
from integral_lib.Comands import read_json from lib.Comands import read_json
_role = await read_json(member.guild.id, 'tigger_role') _role = get(member.guild.roles, id=read_json(member.guild.id, 'tigger_role'))
audio: list = [] audio: list = []
for _a in ListGenerator('audio'): for _a in ListGenerator('audio'):
audio.append(_a.name) audio.append(_a.name)
if len(member.roles) == 1 or _role is None: if len(member.roles) == 1 or _role is None:
logger.info('Skip playing by role') logger.info('Skip playing by role')
elif any(str(role.id) in _role for role in member.roles): elif _role in member.roles:
logger.info('Play audio from list by role') logger.info('Play audio from list by role')
await play_audio(f'audio/{random.choice(audio)}', self.bot, after.channel) await play_audio(f'audio/{random.choice(audio)}', self.bot, after.channel)
else: else:
@@ -54,22 +55,33 @@ class Audio(commands.Cog, name='Audio'):
): ):
if inter.author.voice is not None: if inter.author.voice is not None:
await inter.response.send_message(f'Played {audio}', ephemeral=True) await inter.response.send_message(f'Played {audio}', ephemeral=True)
await play_audio(audio, self.bot, inter.author.voice.channel) await play_audio(audio, self.bot, inter.author.voice.channel)
else: else:
await inter.response.send_message('You`re not in voice', ephemeral=True) await inter.response.send_message('You`re not in voice', ephemeral=True)
@commands.slash_command(name="play_random",
description="Make possible playing audio by command",
options=[
Option(name="num",
type=OptionType.integer,
)
])
async def playrandom(self, inter: disnake.ApplicationCommandInteraction,
num: int
):
if inter.author.voice is not None:
audio: list = []
for _a in ListGenerator('audio'):
audio.append(_a.name)
for i in range(num):
logger.info(f'Played {i+1} times')
await play_audio(f'audio/{random.choice(audio)}', self.bot, inter.author.voice.channel)
else:
await inter.response.send_message('You`re not in voice', ephemeral=True)
@playaudio.autocomplete('audio') @playaudio.autocomplete('audio')
async def list_to_play(self, inter: ApplicationCommandInteraction, current: str): async def list_to_play(self, inter: disnake.ApplicationCommandInteraction, current: str):
"""
Asynchronously generates a list of OptionChoices for the given audio files based on the user input.
Parameters:
- inter: disnake.ApplicationCommandInteraction - The interaction context for the command.
- current: str - The current user input to filter the audio file choices.
Returns:
- list[OptionChoice] - A list of OptionChoice objects representing the available audio file choices.
:param current:
:param inter: ApplicationCommandInteraction
"""
current = current.lower() current = current.lower()
_dict: dict = {} _dict: dict = {}
for f in ListGenerator('audio'): for f in ListGenerator('audio'):

View File

@@ -3,14 +3,14 @@ from typing import List
import disnake import disnake
from disnake import OptionChoice from disnake import OptionChoice
from disnake.ext import commands from disnake.ext import commands
from loguru import logger
from integral_lib.Comands import write_json from lib.Comands import write_json
from integral_lib.Logger import logger
class Fun(commands.Cog, name='Fun'): class Fun(commands.Cog, name='Fun'):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot # defining src as global var in class self.bot = bot # defining bot as global var in class
@commands.Cog.listener() # this is a decorator for events/listeners @commands.Cog.listener() # this is a decorator for events/listeners
async def on_ready(self): async def on_ready(self):
@@ -19,7 +19,7 @@ class Fun(commands.Cog, name='Fun'):
@commands.has_permissions(administrator=True) @commands.has_permissions(administrator=True)
@commands.slash_command( @commands.slash_command(
name="set_bot_channel", name="set_bot_channel",
description="Set channel which iterate with src", description="Set channel which iterate with bot",
) )
async def set_bot_channel(self, inter: disnake.ApplicationCommandInteraction, channel: str): async def set_bot_channel(self, inter: disnake.ApplicationCommandInteraction, channel: str):

View File

@@ -1,14 +1,14 @@
import disnake import disnake
from disnake import Option from disnake import Option
from disnake.ext import commands from disnake.ext import commands
from loguru import logger
from integral_lib import YandexPlayer from lib import YandexPlayer
from integral_lib.Logger import logger
class Testing(commands.Cog, name='Testing'): class Testing(commands.Cog, name='Testing'):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot # defining src as global var in class self.bot = bot # defining bot as global var in class
@commands.Cog.listener() # this is a decorator for events/listeners @commands.Cog.listener() # this is a decorator for events/listeners
async def on_ready(self): async def on_ready(self):

View File

@@ -2,12 +2,13 @@ import disnake
from disnake import Option, OptionType, Colour from disnake import Option, OptionType, Colour
from disnake.ext import commands from disnake.ext import commands
from integral_lib.Logger import logger # from lib.Logger import logger
from loguru import logger
class General(commands.Cog): class General(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot # defining src as global var in class self.bot = bot # defining bot as global var in class
@commands.Cog.listener() # this is a decorator for events/listeners @commands.Cog.listener() # this is a decorator for events/listeners
async def on_ready(self): async def on_ready(self):

View File

@@ -2,30 +2,31 @@ import os
import disnake import disnake
import psutil import psutil
from disnake import ApplicationCommandInteraction
from disnake.ext import commands from disnake.ext import commands
from loguru import logger
from __init__ import version_info as ver from __init__ import version_info as ver
from integral_lib.Comands import determine_prefix from lib.Comands import determine_prefix
from integral_lib.Logger import logger
# from lib.Logger import logger
class BotInfo(commands.Cog, name='Bot Info'): class BotInfo(commands.Cog, name='Bot Info'):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot # defining src as global var in class self.bot = bot # defining bot as global var in class
@commands.Cog.listener() # this is a decorator for events/listeners @commands.Cog.listener() # this is a decorator for events/listeners
async def on_ready(self): async def on_ready(self):
logger.info(f'Cog {__name__.split(".")[1]} is ready!.') logger.info(f'Cog {__name__.split(".")[1]} is ready!.')
@commands.slash_command(name="info_bot", @commands.slash_command(name="info_bot",
description='Shows general info about src') # this is for making a command description='Shows general info about bot') # this is for making a command
async def info_bot(self, inter: ApplicationCommandInteraction): async def info_bot(self, inter: disnake.ApplicationCommandInteraction):
_pid = os.getpid() _pid = os.getpid()
_process = psutil.Process(_pid) _process = psutil.Process(_pid)
emb = disnake.Embed( emb = disnake.Embed(
title="General information", title="General information",
description="General information on about src", description="General information on about bot",
) )
emb.set_thumbnail(self.bot.user.avatar.url) emb.set_thumbnail(self.bot.user.avatar.url)
emb.add_field(name="System info:", value=f"Memory Usage: {round(_process.memory_info().rss / 2 ** 20, 2)} Mb\n" emb.add_field(name="System info:", value=f"Memory Usage: {round(_process.memory_info().rss / 2 ** 20, 2)} Mb\n"

View File

45
lib/CogsPrep.py Normal file
View File

@@ -0,0 +1,45 @@
"""
lib.CogsPrepare
~~~~~~~~~~~~~
Loads, unloads Cogs files
cog_list: return list of cog filenames
work_with_cogs: loads, reloads and unloads cogs files
"""
from os import listdir, rename
from typing import List
from disnake.ext import commands
# from .Logger import logger
from loguru import logger
async def cog_list(fold: str = './cogs') -> List[str]:
cogs_list = []
for _filename in listdir(fold):
if _filename.endswith('.py'):
cogs_list.append(_filename[:-3])
return cogs_list
async def work_with_cogs(what_do, bot: commands.Bot, cog):
if isinstance(cog, str):
cog = cog.split()
for _filename in cog:
if what_do == "load":
bot.load_extension(f'cogs.{_filename}')
logger.info(f'Loaded cog {_filename}')
elif what_do == 'unload':
bot.unload_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} unloaded')
elif what_do == 'reload':
bot.reload_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} reloaded')
elif what_do == 'disable':
bot.unload_extension(f'cogs.{_filename}')
rename(f'cogs/{_filename}.py', f'cogs/disabled/{_filename}.py')
logger.info(f'Cog {_filename} stopped and disabled')
elif what_do == 'enable':
rename(f'cogs/disabled/{_filename}.py', f'cogs/{_filename}.py')
bot.load_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} started and enabled')

View File

@@ -1,9 +1,7 @@
""" """
integral_lib.Commands lib.Commands
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
Some prepare for commands Some prepare for commands
""" """
from json import load, decoder, dump, JSONDecodeError from json import load, decoder, dump, JSONDecodeError
from os import getenv from os import getenv
@@ -11,7 +9,7 @@ from os import getenv
from disnake.ext import commands from disnake.ext import commands
async def read_json(guild: int, _param: str): def read_json(guild: int, _param: str):
""" """
Reads Json file to determite config strings Reads Json file to determite config strings
:param guild: ID of Guild :param guild: ID of Guild
@@ -37,18 +35,7 @@ async def read_json(guild: int, _param: str):
async def write_json(guild: int, param_name: str, param: str or int): async def write_json(guild: int, param_name: str, param: str or int):
""" print(type(param))
A function to write JSON data to a file after updating or adding a parameter value.
Parameters:
- guild: an integer representing the guild ID
- param_name: a string representing the parameter name
- param: a string or integer representing the parameter value
Returns:
This function does not return anything.
"""
with open(getenv('CONF_FILE'), encoding='utf-8') as f: with open(getenv('CONF_FILE'), encoding='utf-8') as f:
try: try:
_json = load(f) _json = load(f)
@@ -59,15 +46,15 @@ async def write_json(guild: int, param_name: str, param: str or int):
except KeyError: except KeyError:
_json.update({f'{guild}': {}}) _json.update({f'{guild}': {}})
_guild = _json[f'{guild}'] _guild = _json[f'{guild}']
_guild.update({f'{param_name}': f'{param}'}) _guild.update({f'{param_name}': param})
_json.update({f'{guild}': _guild})
with open(getenv('CONF_FILE'), 'w', encoding='utf-8') as f: with open(getenv('CONF_FILE'), 'w', encoding='utf-8') as f:
dump(_json, f, indent=4) dump(_json, f, indent=4)
def determine_prefix(bot: commands.Bot, msg): def determine_prefix(bot: commands.Bot, msg):
""" """
Determine the per-server src prefix based on the given message object. Determite per-server bot prefix
:param bot: Disnake Bot object :param bot: Disnake Bot object
:param msg: Disnake msg object :param msg: Disnake msg object
:return: prefix for server, default is $ :return: prefix for server, default is $

View File

@@ -1,6 +1,7 @@
from sqlite3 import connect, Error from sqlite3 import connect, Error
from .Logger import logger # from lib.Logger import logger
from loguru import logger
class DBReader: class DBReader:
@@ -18,7 +19,8 @@ class DBReader:
try: try:
sql_con = connect("user.db") sql_con = connect("user.db")
cursor = sql_con.cursor() cursor = sql_con.cursor()
cursor.execute(f"""SELECT * FROM "{guildid}" """) cursor.execute("""SELECT * FROM "(guildid)" """,
{'guildid': guildid})
record = cursor.fetchall() record = cursor.fetchall()
return record return record
except Error as _e: except Error as _e:
@@ -85,9 +87,9 @@ async def prepare_db(guild: int):
try: try:
_connect = connect('user.db') _connect = connect('user.db')
cursor = _connect.cursor() cursor = _connect.cursor()
cursor.execute(f'CREATE TABLE IF NOT EXISTS "{guild}"\n' cursor.execute(f'''CREATE TABLE IF NOT EXISTS "{guild}"
f' ([userid] INTEGER PRIMARY KEY, [username] TEXT,\n' ([userid] INTEGER PRIMARY KEY, [username] TEXT,
f' [nick] TEXT, [isbot] BOOL, [defaulttracks] TEXT, [usertracks] TEXT)') [nick] TEXT, [isbot] BOOL, [defaulttracks] TEXT, [usertracks] TEXT)''')
cursor.close() cursor.close()
except Error as _error: except Error as _error:
logger.info(_error) logger.info(_error)
@@ -110,7 +112,7 @@ async def work_with_db(db_func: str, data_turple: tuple):
async def fill_bd(name: str, userid: int, isbot: bool, nick: str, guild: int): async def fill_bd(name: str, userid: int, isbot: bool, nick: str, guild: int):
sqlite_insert_with_param = (f"""INSERT OR IGNORE INTO '{guild}' sqlite_insert_with_param = (f"""INSERT OR IGNORE INTO "{guild}"
(username, userid, nick, isbot) (username, userid, nick, isbot)
VALUES (?, ?, ?, ?)""") VALUES (?, ?, ?, ?)""")
data_tuple: tuple[str, int, str, bool] = (name, userid, nick, isbot) data_tuple: tuple[str, int, str, bool] = (name, userid, nick, isbot)

View File

@@ -1,16 +1,19 @@
from asyncio import sleep from asyncio import sleep
from disnake import FFmpegPCMAudio from disnake import FFmpegOpusAudio
from loguru import logger
from .Logger import logger
# from .Logger import logger
@logger.catch
async def play_audio(audio, bot, vc): async def play_audio(audio, bot, vc):
if not bot.voice_clients: if not bot.voice_clients:
logger.error(f'Playing: {audio}') logger.error(f'Playing: {audio}')
vp = await vc.connect() vp = await vc.connect()
if not vp.is_playing(): if not vp.is_playing():
vp.play(FFmpegPCMAudio(f'{audio}', )) vp.play(FFmpegOpusAudio(f'{audio}', executable='ffmpeg', options='-nostats -loglevel 0'))
while vp.is_playing(): while vp.is_playing():
await sleep(0.5) await sleep(0.5)
await sleep(1) await sleep(1)

5
lib/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
"""
lib
~~~~~~~~~~~~~
Some libs for the bot which help him
"""

6
qodana.yaml Normal file
View File

@@ -0,0 +1,6 @@
version: "1.0"
bootstrap: |
rm -rf .idea
pip install -U -r requirements.txt
profile:
name: qodana.recommended

Binary file not shown.

View File

@@ -1,78 +0,0 @@
"""
integral_lib.CogsPrepare
~~~~~~~~~~~~~
Loads, unloads Cogs files
cog_list: return list of cog filenames
work_with_cogs: loads, reloads and unloads cogs files
"""
from os import listdir, rename
from typing import List
from disnake.ext.commands.interaction_bot_base import InteractionBotBase
from .Logger import logger
def cog_list(fold: str = './cogs') -> List[str]:
"""
A function that generates a list of cog names based on the files present in a specified folder.
Parameters:
- fold (str): The directory path where the cog files are located. Defaults to './cogs'.
Returns:
- List[str]: A list of cog names without the '.py' extension.
"""
cogs_list = []
for _filename in listdir(fold):
if _filename.endswith('.py'):
cogs_list.append(_filename[:-3])
return cogs_list
def work_with_cogs(what_do: str,
bot: InteractionBotBase,
cog: str | list):
"""
Perform the specified action on the given cog or list of cogs.
Args:
what_do (str): The action to perform (load, unload, reload, disable, enable).
src (InteractionBotBase): The src instance to work with.
cog (str | list): The name of the cog or a list of cogs to work with.
Raises:
ValueError: If the action is not recognized.
Returns:
None
--------
:param cog: str | list
:param bot: InteractionBotBase
:param what_do: str = ['load', 'unload', 'reload', 'disable', 'enable']
"""
if isinstance(cog, str):
cog = cog.split()
for _filename in cog:
if _filename.endswith('.py'):
_filename = _filename.split('.')[0]
if what_do == "load":
bot.load_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} loaded')
elif what_do == 'unload':
bot.unload_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} unloaded')
elif what_do == 'reload':
bot.reload_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} reloaded')
elif what_do == 'disable':
bot.unload_extension(f'cogs.{_filename}')
rename(f'cogs/{_filename}.py',
f'cogs/disabled/{_filename}.py')
logger.info(f'Cog {_filename} stopped and disabled')
elif what_do == 'enable':
rename(f'cogs/disabled/{_filename}.py',
f'cogs/{_filename}.py')
bot.load_extension(f'cogs.{_filename}')
logger.info(f'Cog {_filename} started and enabled')
else:
raise ValueError(f"Unrecognized action: {what_do}")

View File

@@ -1,52 +0,0 @@
import logging
class _CustomFormatter(logging.Formatter):
grey = "\x1b[38;20m"
yellow = "\x1b[33;20m"
red = "\x1b[31;20m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
fmt = "%(asctime)s - [%(levelname)s] -%(module)s- %(message)s"
FORMATS = {
logging.DEBUG: grey + fmt + reset,
logging.INFO: grey + fmt + reset,
logging.WARNING: yellow + fmt + reset,
logging.ERROR: red + fmt + reset,
logging.CRITICAL: bold_red + fmt + reset
}
old_factory = logging.getLogRecordFactory()
def _record_factory(*args, **kwargs):
record = _CustomFormatter.old_factory(*args, **kwargs)
_module = record.module
_levelname = record.levelname
if len(_module) % 2 == 0 and len(_module) < 12:
_module = ' ' * ((12 - len(_module)) // 2) + _module + ' ' * ((12 - len(_module)) // 2)
elif len(_module) % 2 == 1 and len(_module) < 12:
_module = ' ' * ((12 - len(_module)) // 2) + record.module + ' ' * ((12 - len(_module)) // 2 + 1)
if len(_levelname) % 2 == 0 and len(_levelname) < 8:
_levelname = ' ' * ((8 - len(_levelname)) // 2) + _levelname + ' ' * ((8 - len(_levelname)) // 2)
elif len(record.levelname) % 2 == 1 and len(record.module) < 8:
_levelname = ' ' * ((8 - len(_levelname)) // 2) + _levelname + ' ' * ((8 - len(_levelname)) // 2 + 1)
record.module = _module
record.levelname = _levelname
return record
logging.setLogRecordFactory(_record_factory)
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt, "%d-%m-%Y %H:%M:%S")
return formatter.format(record)
logger = logging.getLogger('Pisya_Bot')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(_CustomFormatter())
logger.addHandler(ch)

View File

@@ -1,5 +0,0 @@
"""
integral_lib
~~~~~~~~~~~~~
Some libs for the src which help him
"""

Binary file not shown.

View File

@@ -1,41 +0,0 @@
from disnake import Intents
from disnake.ext import commands
intents = Intents(messages=True,
message_content=True,
)
class Cog(commands.Cog):
@commands.command()
async def my_cmd(self, ctx):
pass
class CogTwo(commands.Cog):
async def bot_check(self, ctx) -> bool:
pass
class CogThree(commands.Cog):
async def bot_check_once(self, ctx) -> bool:
pass
class CogFour(commands.Cog):
async def bot_slash_command_check(self, ctx) -> bool:
pass
class CogFive(commands.Cog):
async def can_run(self, ctx) -> bool:
pass
def test_bot():
bot = commands.Bot(command_prefix='$', intents=intents)
bot.add_cog(Cog())
bot.add_cog(CogTwo())
bot.add_cog(CogThree())
bot.add_cog(CogFour())
bot.add_cog(CogFive())

View File

@@ -1,31 +0,0 @@
import sys
import pytest
from disnake.ext.commands.common_bot_base import CommonBotBase
from mock import mock
sys.path.append('src')
from integral_lib import CogsPrep
def test_cog_list():
with mock.patch('integral_lib.CogsPrep.listdir') as MockClass:
MockClass.return_value = ['cog1.py', 'cog2.py']
result = CogsPrep.cog_list()
assert result == ['cog1', 'cog2']
@pytest.mark.parametrize("cog", ["cog1.py", "cog2"])
@pytest.mark.parametrize("what_do", ['load', 'unload', 'reload', 'disable', 'enable'])
def test_work_with_cogs(what_do, cog):
with mock.patch('integral_lib.CogsPrep.rename') as mock_rename:
mock_rename.return_value = None
mock_bot = mock.MagicMock(spec=CommonBotBase)
result = CogsPrep.work_with_cogs(what_do, mock_bot, cog)
if what_do in ['load', 'enable']:
assert mock_bot.load_extension.called
elif what_do in ['unload', 'disable']:
assert mock_bot.unload_extension.called
elif what_do == 'reload':
assert mock_bot.reload_extension.called