upload from local
upload muzovkantv2 from local storage
This commit is contained in:
0
cogs/__init__.py
Normal file
0
cogs/__init__.py
Normal file
BIN
cogs/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/funchosa_parser.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/funchosa_parser.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/help.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/help.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/kitty.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/kitty.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/muter.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/muter.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/role_manager.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/role_manager.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/status_rotator.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/status_rotator.cpython-314.pyc
Normal file
Binary file not shown.
BIN
cogs/__pycache__/uptime.cpython-314.pyc
Normal file
BIN
cogs/__pycache__/uptime.cpython-314.pyc
Normal file
Binary file not shown.
322
cogs/funchosa_parser.py
Normal file
322
cogs/funchosa_parser.py
Normal file
@@ -0,0 +1,322 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
from discord import app_commands
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
import config
|
||||
from utils.database import FunchosaDatabase
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class FunchosaParser(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.db = FunchosaDatabase()
|
||||
|
||||
self.target_channel_id = config.FUNCHOSA_CHANNEL_ID
|
||||
self.is_parsing = False
|
||||
self.parsed_count = 0
|
||||
self.rate_limit_delay = 0.5
|
||||
|
||||
async def cog_load(self):
|
||||
await self.db.init_db()
|
||||
logger.info("[FunchosaParser] cog initialized")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
await asyncio.sleep(10)
|
||||
|
||||
if not self.is_parsing:
|
||||
await self.auto_parse_on_startup()
|
||||
|
||||
async def auto_parse_on_startup(self):
|
||||
try:
|
||||
if not self.target_channel_id:
|
||||
logger.warning("[FunchosaParser] no id channel ")
|
||||
return
|
||||
|
||||
channel = self.bot.get_channel(self.target_channel_id)
|
||||
if not channel:
|
||||
logger.warning(f"[FunchosaParser] no channel with id {self.target_channel_id} found")
|
||||
return
|
||||
|
||||
status = await self.db.get_parsing_status()
|
||||
logger.info(f"[FunchosaParser] parsing status; firsttry = {not status['first_parse_done']}")
|
||||
|
||||
if self.is_parsing:
|
||||
logger.warning("[FunchosaParser] parsing already in progress")
|
||||
return
|
||||
|
||||
logger.info("[FunchosaParser] starting to parse")
|
||||
|
||||
if not status['first_parse_done']:
|
||||
logger.info("[FunchosaParser] first try, parse all")
|
||||
count = await self._parse_history(channel, limit=None)
|
||||
|
||||
last_message_id = await self.db.get_last_message_in_db()
|
||||
await self.db.update_parsing_status(
|
||||
first_parse_done=True,
|
||||
last_parsed_message_id=last_message_id
|
||||
)
|
||||
|
||||
logger.info(f"[FunchosaParser] parsing finished, total msg count: {count}")
|
||||
else:
|
||||
logger.info("[FunchosaParser] NOTfirst try, parse first 250")
|
||||
count = await self._parse_history(channel, limit=250)
|
||||
|
||||
if count > 0:
|
||||
new_last_message_id = await self.db.get_last_message_in_db()
|
||||
await self.db.update_parsing_status(
|
||||
first_parse_done=True,
|
||||
last_parsed_message_id=new_last_message_id
|
||||
)
|
||||
|
||||
logger.info(f"[FunchosaParser] parsing finished, total msg count: {count}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[FunchosaParser] err when parsing: {e}", exc_info=True)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message):
|
||||
if message.author.bot:
|
||||
return
|
||||
|
||||
if self.target_channel_id and message.channel.id == self.target_channel_id:
|
||||
await self._save_message(message)
|
||||
|
||||
async def _save_message(self, message):
|
||||
try:
|
||||
if await self.db.message_exists(message.id):
|
||||
return
|
||||
|
||||
attachments_data = []
|
||||
attachment_urls = []
|
||||
|
||||
for attachment in message.attachments:
|
||||
if attachment.url.lower().endswith(('png', 'jpg', 'jpeg', 'gif', 'webp')):
|
||||
attachments_data.append({
|
||||
'url': attachment.url,
|
||||
'filename': attachment.filename
|
||||
})
|
||||
attachment_urls.append(attachment.url)
|
||||
|
||||
message_data = {
|
||||
'message_id': message.id,
|
||||
'channel_id': message.channel.id,
|
||||
'author_id': message.author.id,
|
||||
'author_name': str(message.author),
|
||||
'content': message.content,
|
||||
'timestamp': message.created_at.isoformat(),
|
||||
'message_url': message.jump_url,
|
||||
'has_attachments': len(message.attachments) > 0,
|
||||
'attachment_urls': ','.join(attachment_urls),
|
||||
'attachments': attachments_data
|
||||
}
|
||||
|
||||
saved = await self.db.save_message(message_data)
|
||||
if saved:
|
||||
self.parsed_count += 1
|
||||
if self.parsed_count % 50 == 0:
|
||||
logger.info(f"[FunchosaParser] saved messages total: {self.parsed_count}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[FunchosaParser] err when saving msg: {e}")
|
||||
|
||||
async def _parse_history(self, channel, limit=None, after_message=None):
|
||||
try:
|
||||
self.is_parsing = True
|
||||
count = 0
|
||||
skipped = 0
|
||||
batch_size = 0
|
||||
|
||||
logger.info(f"[FunchosaParser] starting to parse {channel.name}")
|
||||
|
||||
oldest_first = not limit or limit < 0
|
||||
|
||||
parse_kwargs = {
|
||||
'limit': abs(limit) if limit else None,
|
||||
'oldest_first': oldest_first,
|
||||
}
|
||||
|
||||
if after_message:
|
||||
parse_kwargs['after'] = after_message
|
||||
|
||||
async for message in channel.history(**parse_kwargs):
|
||||
if message.author.bot:
|
||||
continue
|
||||
|
||||
if await self.db.message_exists(message.id):
|
||||
skipped += 1
|
||||
batch_size += 1
|
||||
|
||||
if batch_size >= 100:
|
||||
logger.info(f"[FunchosaParser] batch: +{count} новых, -{skipped} skipped")
|
||||
batch_size = 0
|
||||
|
||||
continue
|
||||
|
||||
await self._save_message(message)
|
||||
count += 1
|
||||
batch_size += 1
|
||||
|
||||
await asyncio.sleep(self.rate_limit_delay)
|
||||
|
||||
if batch_size >= 100:
|
||||
logger.info(f"[FunchosaParser] batch: +{count} новых, -{skipped} skipped")
|
||||
batch_size = 0
|
||||
|
||||
logger.info(f"[FunchosaParser] parsing done. total new: {count}, total skipped: {skipped}")
|
||||
return count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[FunchosaParser] err when parsing history: {e}", exc_info=True)
|
||||
return 0
|
||||
finally:
|
||||
self.is_parsing = False
|
||||
|
||||
@commands.hybrid_command()
|
||||
@app_commands.describe(
|
||||
number="номер сообщения из базы; optional"
|
||||
)
|
||||
async def funchosarand(self, ctx, number: Optional[int] = None):
|
||||
await ctx.defer()
|
||||
|
||||
if number:
|
||||
message_data = await self.db.get_message_by_number(number)
|
||||
if not message_data:
|
||||
await ctx.send(f"сообщение с номером {number} не найдено в базе ||соси черт||")
|
||||
return
|
||||
else:
|
||||
message_data = await self.db.get_random_message()
|
||||
if not message_data:
|
||||
await ctx.send("помоему чет поломалось. меня пингани")
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
description=message_data['content'] or "*[без текста]*",
|
||||
color=discord.Color.blue(),
|
||||
timestamp=datetime.fromisoformat(message_data['timestamp'])
|
||||
)
|
||||
|
||||
embed.set_author(
|
||||
name='random фунчоза of the day'
|
||||
)
|
||||
|
||||
|
||||
attachments_text = []
|
||||
if message_data.get('attachments'):
|
||||
for i, att in enumerate(message_data['attachments'][:3], 1):
|
||||
attachments_text.append(f"[вложение {i}]({att['url']})")
|
||||
|
||||
embed.add_field(
|
||||
name="info",
|
||||
value=(
|
||||
f"автор: <@{message_data['author_id']}>\n"
|
||||
f"дата: {message_data['timestamp'].replace('T', ' ')[:19]}\n"
|
||||
f"номер в базе: {message_data['id']}"
|
||||
),
|
||||
inline=False
|
||||
)
|
||||
|
||||
if attachments_text:
|
||||
embed.add_field(
|
||||
name="вложения",
|
||||
value="\n".join(attachments_text),
|
||||
inline=False
|
||||
)
|
||||
|
||||
view = discord.ui.View()
|
||||
view.add_item(
|
||||
discord.ui.Button(
|
||||
label="перейти к сообщению",
|
||||
url=message_data['message_url'],
|
||||
style=discord.ButtonStyle.link
|
||||
)
|
||||
)
|
||||
|
||||
view.add_item(
|
||||
discord.ui.Button(
|
||||
label="подавай еще, раб",
|
||||
custom_id="another_random",
|
||||
style=discord.ButtonStyle.secondary
|
||||
)
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed, view=view)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_interaction(self, interaction: discord.Interaction):
|
||||
if not interaction.data or 'custom_id' not in interaction.data:
|
||||
return
|
||||
|
||||
if interaction.data['custom_id'] == "another_random":
|
||||
await interaction.response.defer()
|
||||
|
||||
message_data = await self.db.get_random_message()
|
||||
if not message_data:
|
||||
await interaction.followup.send("помоему чет поломалось. меня пингани", ephemeral=True)
|
||||
return
|
||||
|
||||
embed = discord.Embed(
|
||||
description=message_data['content'] or "*[без текста]*",
|
||||
color=discord.Color.blue(),
|
||||
timestamp=datetime.fromisoformat(message_data['timestamp'])
|
||||
)
|
||||
|
||||
embed.set_author(
|
||||
name='random фунчоза of the day'
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name="info",
|
||||
value=(
|
||||
f"автор: <@{message_data['author_id']}>\n"
|
||||
f"дата: {message_data['timestamp'].replace('T', ' ')[:19]}\n"
|
||||
f"номер в базе: {message_data['id']}"
|
||||
),
|
||||
inline=False
|
||||
)
|
||||
|
||||
view = discord.ui.View()
|
||||
view.add_item(
|
||||
discord.ui.Button(
|
||||
label="перейти к сообщению",
|
||||
url=message_data['message_url'],
|
||||
style=discord.ButtonStyle.link
|
||||
)
|
||||
)
|
||||
view.add_item(
|
||||
discord.ui.Button(
|
||||
label="подавай еще, раб",
|
||||
custom_id="another_random",
|
||||
style=discord.ButtonStyle.secondary
|
||||
)
|
||||
)
|
||||
|
||||
await interaction.followup.send(embed=embed, view=view)
|
||||
|
||||
@commands.hybrid_command()
|
||||
async def funchosainfo(self, ctx):
|
||||
total = await self.db.get_total_count()
|
||||
status = await self.db.get_parsing_status()
|
||||
|
||||
embed = discord.Embed(
|
||||
title="фунчоза.статы",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
|
||||
embed.add_field(name="сообщений в базе", value=f"**{total}**", inline=True)
|
||||
|
||||
if status['last_parsed_message_id']:
|
||||
embed.add_field(
|
||||
name="последнее сообщение",
|
||||
value=f"id: `{status['last_parsed_message_id']}`",
|
||||
inline=False
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(FunchosaParser(bot))
|
||||
27
cogs/help.py
Normal file
27
cogs/help.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
class HelpCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(name="help")
|
||||
async def help(self, ctx):
|
||||
help_text = """
|
||||
комманды:
|
||||
|
||||
```
|
||||
uptime : сколько времени прошло с запуска бота
|
||||
funchosarand <id> : рандомная пикча из фунчозы либо по айдишнику в базе
|
||||
funchosainfo : фунчоза.статы
|
||||
kitty : рандомная пикча кошечки из [thecatapi](https://thecatapi.com/)
|
||||
```
|
||||
|
||||
префикс: `!`
|
||||
в лс отпишите по предложениям че в бота докинуть
|
||||
"""
|
||||
|
||||
await ctx.send(help_text)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(HelpCog(bot))
|
||||
87
cogs/kitty.py
Normal file
87
cogs/kitty.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import aiohttp
|
||||
import os
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Kitty(commands.Cog, name="Котики"):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.api_key = os.environ.get('CAT_API_KEY')
|
||||
self.search_url = "https://api.thecatapi.com/v1/images/search"
|
||||
|
||||
if not self.api_key:
|
||||
logger.warning("[Kitty] no api key found")
|
||||
|
||||
async def _fetch_random_cat(self):
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
if self.api_key and self.api_key != "DEMO-API-KEY":
|
||||
headers["x-api-key"] = self.api_key
|
||||
else:
|
||||
headers["x-api-key"] = "DEMO-API-KEY"
|
||||
|
||||
params = {
|
||||
'size': 'med',
|
||||
'mime_types': 'jpg,png',
|
||||
'format': 'json',
|
||||
'has_breeds': 'true',
|
||||
'order': 'RANDOM',
|
||||
'limit': 1
|
||||
}
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self.search_url, headers=headers, params=params) as response:
|
||||
if response.status == 200:
|
||||
data = await response.json()
|
||||
if data and isinstance(data, list):
|
||||
logger.info(f"[Kitty] API response received")
|
||||
return data[0]
|
||||
else:
|
||||
logger.error(f"[Kitty] api err: {response.status}")
|
||||
|
||||
try:
|
||||
error_text = await response.text()
|
||||
logger.error(f"[Kitty] api error text: {error_text}")
|
||||
except:
|
||||
pass
|
||||
except aiohttp.ClientError as e:
|
||||
logger.error(f"[Kitty] client error when contacting api: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"[Kitty] err: {e}")
|
||||
return None
|
||||
|
||||
@commands.hybrid_command(name="kitty", description="kitty")
|
||||
async def kitty(self, ctx):
|
||||
await ctx.defer()
|
||||
logger.info(f"[Kitty] kitty request from {ctx.author.name} ({ctx.author.id})")
|
||||
|
||||
cat_data = await self._fetch_random_cat()
|
||||
|
||||
if not cat_data:
|
||||
logger.warning("[Kitty] cat_data = null")
|
||||
await ctx.send("помоему чет поломалось. меня пингани ||not cat_data||")
|
||||
return
|
||||
|
||||
image_url = cat_data.get('url')
|
||||
if not image_url:
|
||||
logger.error("[Kitty] no image url")
|
||||
await ctx.send("помоему чет поломалось. меня пингани ||no image url||")
|
||||
return
|
||||
|
||||
|
||||
breeds_info = cat_data.get('breeds')
|
||||
if breeds_info and len(breeds_info) > 0:
|
||||
breed = breeds_info[0]
|
||||
if breed.get('name'):
|
||||
caption = f"{breed['name']}"
|
||||
logger.info(f"[Kitty] Breed found: {breed['name']}")
|
||||
|
||||
await ctx.send(f"random kitty of the day\n[{caption}]({image_url})")
|
||||
logger.info(f"[Kitty] succesfully send kitty to {ctx.author.name}")
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(Kitty(bot))
|
||||
0
cogs/quote.py
Normal file
0
cogs/quote.py
Normal file
147
cogs/role_manager.py
Normal file
147
cogs/role_manager.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from discord import app_commands
|
||||
import config
|
||||
from utils.data_manager import save_message_id, load_message_id
|
||||
import logging
|
||||
|
||||
# Создаем логгер для этого модуля
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class RoleManager(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.role_message_id = None
|
||||
self.CHANNEL_ID = config.CHANNEL_ID
|
||||
self.REACTION_ROLES = config.REACTION_ROLES
|
||||
self._ready = False
|
||||
|
||||
async def cog_load(self):
|
||||
self.role_message_id = load_message_id()
|
||||
if self.role_message_id:
|
||||
logger.info(f"[RoleManager] initialized role msg with id: '{self.role_message_id}'")
|
||||
else:
|
||||
logger.info('[RoleManager] no role msg found')
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
if not self._ready and self.role_message_id:
|
||||
await self.check_and_sync_roles()
|
||||
self._ready = True
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_add(self, payload):
|
||||
await self.handle_reaction(payload, add_role=True)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_raw_reaction_remove(self, payload):
|
||||
await self.handle_reaction(payload, add_role=False)
|
||||
|
||||
async def handle_reaction(self, payload, add_role=True):
|
||||
if payload.message_id != self.role_message_id:
|
||||
return
|
||||
|
||||
emoji = str(payload.emoji)
|
||||
if emoji not in self.REACTION_ROLES:
|
||||
return
|
||||
|
||||
guild = self.bot.get_guild(payload.guild_id)
|
||||
if not guild:
|
||||
return
|
||||
|
||||
member = guild.get_member(payload.user_id)
|
||||
if not member or member.bot:
|
||||
return
|
||||
|
||||
role_id = self.REACTION_ROLES[emoji]
|
||||
role = guild.get_role(role_id)
|
||||
|
||||
if not role:
|
||||
logger.warning(f"[RoleManager] role with id '{role_id}' not found")
|
||||
return
|
||||
|
||||
try:
|
||||
if add_role:
|
||||
await member.add_roles(role)
|
||||
logger.info(f"[RoleManager] gave role '{role.name}' to '{member.name}'")
|
||||
else:
|
||||
await member.remove_roles(role)
|
||||
logger.info(f"[RoleManager] removed role '{role.name}' from user '{member.name}'")
|
||||
except discord.Forbidden:
|
||||
logger.error(f"[RoleManager] not enough rights to give role '{role.name}'")
|
||||
except Exception as e:
|
||||
logger.error(f"[RoleManager] err: '{e}'")
|
||||
|
||||
async def check_and_sync_roles(self):
|
||||
if not self.role_message_id:
|
||||
return
|
||||
|
||||
try:
|
||||
channel = await self.bot.fetch_channel(self.CHANNEL_ID)
|
||||
if not channel:
|
||||
logger.warning(f"[RoleManager] channel with id '{self.CHANNEL_ID}' not found")
|
||||
return
|
||||
|
||||
message = await channel.fetch_message(self.role_message_id)
|
||||
|
||||
for reaction in message.reactions:
|
||||
emoji = str(reaction.emoji)
|
||||
|
||||
if emoji in self.REACTION_ROLES:
|
||||
role_id = self.REACTION_ROLES[emoji]
|
||||
role = message.guild.get_role(role_id)
|
||||
|
||||
if not role:
|
||||
logger.warning(f"[RoleManager] role with id '{role_id}' not found")
|
||||
continue
|
||||
|
||||
async for user in reaction.users():
|
||||
if user.bot:
|
||||
continue
|
||||
|
||||
member = message.guild.get_member(user.id)
|
||||
if member and role not in member.roles:
|
||||
await member.add_roles(role)
|
||||
logger.info(f"[RoleManager] gave role '{role.name}' to user '{member.name}'")
|
||||
|
||||
except discord.NotFound:
|
||||
logger.warning("[RoleManager] role msg not found")
|
||||
except discord.Forbidden:
|
||||
logger.error("[RoleManager] no rights to get channel or message")
|
||||
except Exception as e:
|
||||
logger.error(f"[RoleManager] sync err: '{e}'")
|
||||
|
||||
@commands.hybrid_command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def create_role_message(self, ctx):
|
||||
embed = discord.Embed(
|
||||
title="ле",
|
||||
description="пикните там роли снизу по реактам,\nшоб если куда то шли играть с фаршем >3 человек то сразу можно было ее пингануть\n\n"
|
||||
"react_index = {\n"
|
||||
" '💩': гревдиггер\n"
|
||||
" '🤙': бара наверн хз\n"
|
||||
" '🤕': пох ваще за любой движ\n"
|
||||
" '🇺🇦': мтк\n"
|
||||
"}\n\n"
|
||||
"естессно кто будет спамить пингом ролей тот будет опущен и закинут в таймаут\n"
|
||||
"если бот в оффе роли выдадутся когда я врублю его снова",
|
||||
color=0x00ff00
|
||||
)
|
||||
|
||||
message = await ctx.send(embed=embed)
|
||||
|
||||
for emoji in self.REACTION_ROLES.keys():
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
self.role_message_id = message.id
|
||||
save_message_id(message.id)
|
||||
logger.info(f"[RoleManager] created new role message with id: '{message.id}'")
|
||||
|
||||
@commands.hybrid_command()
|
||||
@commands.has_permissions(administrator=True)
|
||||
async def sync_roles(self, ctx):
|
||||
await self.check_and_sync_roles()
|
||||
logger.info("[RoleManager] manual sync triggered by admin")
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(RoleManager(bot))
|
||||
65
cogs/status_rotator.py
Normal file
65
cogs/status_rotator.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import discord
|
||||
from discord.ext import commands, tasks
|
||||
import json
|
||||
import random
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class StatusRotator(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.statuses: list[str] = []
|
||||
self.current_index = 0
|
||||
self.status_file = 'data/statuses.json'
|
||||
|
||||
async def cog_load(self):
|
||||
await self.load_statuses()
|
||||
self.rotate_status.start()
|
||||
logger.info("[StatusRotator] status rotator initialized")
|
||||
|
||||
async def load_statuses(self):
|
||||
try:
|
||||
with open(self.status_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
self.statuses = data.get('statuses', [])
|
||||
|
||||
logger.info(f"[StatusRotator] loaded {len(self.statuses)} statuses")
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error(f"[StatusRotator] file {self.status_file} notfound")
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"[StatusRotaror] err while parsing JSON")
|
||||
|
||||
def get_random_status(self) -> str:
|
||||
return random.choice(self.statuses)
|
||||
|
||||
def get_next_status(self) -> str:
|
||||
status = self.statuses[self.current_index]
|
||||
self.current_index = (self.current_index + 1) % len(self.statuses)
|
||||
return status
|
||||
|
||||
async def update_status(self, status_text: Optional[str] = None):
|
||||
if status_text is None:
|
||||
status_text = self.get_random_status()
|
||||
|
||||
activity = discord.Game(name=status_text)
|
||||
|
||||
try:
|
||||
await self.bot.change_presence(activity=activity)
|
||||
logger.debug(f"[StatusRotator] status updated: {status_text}")
|
||||
except Exception as e:
|
||||
logger.error(f"[StatusRotator] err while updating status: {e}")
|
||||
|
||||
@tasks.loop(minutes=1.0)
|
||||
async def rotate_status(self):
|
||||
await self.update_status()
|
||||
|
||||
@rotate_status.before_loop
|
||||
async def before_rotate_status(self):
|
||||
await self.bot.wait_until_ready()
|
||||
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(StatusRotator(bot))
|
||||
46
cogs/uptime.py
Normal file
46
cogs/uptime.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import datetime
|
||||
|
||||
class UptimeSimple(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.start_time = None
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
if self.start_time is None:
|
||||
self.start_time = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
@commands.command(name="uptime")
|
||||
async def uptime(self, ctx):
|
||||
if self.start_time is None:
|
||||
await ctx.send("ебать у тебя тайминги кнш")
|
||||
return
|
||||
|
||||
current_time = datetime.datetime.now(datetime.timezone.utc)
|
||||
uptime = current_time - self.start_time
|
||||
seconds = int(uptime.total_seconds())
|
||||
|
||||
days = seconds // 86400
|
||||
hours = (seconds % 86400) // 3600
|
||||
minutes = (seconds % 3600) // 60
|
||||
secs = seconds % 60
|
||||
|
||||
result = "бот работает уже: "
|
||||
parts = []
|
||||
|
||||
if days > 0:
|
||||
parts.append(f"{days} дня")
|
||||
if hours > 0:
|
||||
parts.append(f"{hours} часа")
|
||||
if minutes > 0:
|
||||
parts.append(f"{minutes} минут")
|
||||
if secs > 0 or not parts:
|
||||
parts.append(f"{secs} секунд")
|
||||
|
||||
result += " ".join(parts)
|
||||
await ctx.send(result)
|
||||
|
||||
async def setup(bot):
|
||||
await bot.add_cog(UptimeSimple(bot))
|
||||
Reference in New Issue
Block a user