23 Commits

Author SHA1 Message Date
4afcbc93d5 Merge pull request 'Fixed minor issues' (#6) from dev into main
Reviewed-on: #6
2024-06-23 13:14:08 +03:00
4f610fc55c Fixed minor issues 2024-06-23 12:12:34 +02:00
72ccaa04a4 Merge pull request 'v0.1.0-rc.1' (#5) from dev into main
Reviewed-on: #5
2024-06-23 13:06:12 +03:00
f67375ff4f Closes #2 2024-06-23 12:05:03 +02:00
d168821fb5 Merge branch 'renovate/apscheduler-3.x' into dev 2024-06-23 11:24:42 +02:00
f01047cc11 Merge pull request 'Update dependency aiofiles to v23.2.1' (#4) from renovate/aiofiles-23.x into dev
Reviewed-on: #4
2024-06-23 12:19:43 +03:00
ed190bc07e Update dependency aiofiles to v23.2.1 2024-06-23 12:17:14 +03:00
e14c93f07f Update dependency apscheduler to v3.10.4 2024-06-23 12:17:10 +03:00
ffb3153a47 WIP: Analytics validation 2023-05-08 19:27:33 +02:00
64630e0ab7 Fixed sticker object type 2023-05-08 19:27:22 +02:00
967d5e981e Improved typing 2023-05-08 16:39:46 +02:00
42b4578d36 Slightly changes naming 2023-05-08 15:48:15 +02:00
daae3db305 WIP: Validation 2023-05-08 15:45:16 +02:00
786bc95eeb Changed admin check 2023-05-08 15:45:00 +02:00
35ee903abb Added /action command 2023-05-07 11:01:28 +02:00
16f3d4bc56 Improved analytics 2023-05-07 09:57:35 +02:00
1d902c75e0 Removed deprecated shit 2023-05-07 09:42:21 +02:00
8b6bab9c31 Fixed formatting 2023-05-06 19:17:40 +02:00
df1732fa97 Added custom activity types 2023-05-06 19:06:04 +02:00
5ff49ed052 Sorted, formatted, removed unused imports 2023-05-06 18:56:01 +02:00
640ab65e7a Improved data usage handling 2023-05-06 18:48:04 +02:00
9d7a13f473 Fixed not working config 2023-05-06 18:31:23 +02:00
f9a8d6ddf6 Fixed unexpected behavior 2023-05-06 18:31:07 +02:00
19 changed files with 558 additions and 247 deletions

View File

@@ -1,16 +1,11 @@
from datetime import datetime, timezone
import logging
from random import randint
from typing import Union, Any
from typing import Any, Union
import discord
import discord.member
from modules.utils import config_get
from modules.database import col_users, col_warnings
try:
from typing import Literal
except ImportError:
from typing_extensions import Literal
from modules.database import col_users, col_warnings
from modules.utils import config_get
logger = logging.getLogger(__name__)
@@ -57,108 +52,10 @@ class HoloUser:
self.db_id = jav_user["_id"]
# self.xp = jav_user["xp"]
# self.xp_next = jav_user["xp_next"]
# self.level = jav_user["level"]
# self.work_xp = jav_user["work_xp"]
# self.balance = jav_user["balance"]
self.customrole = jav_user["customrole"]
self.customchannel = jav_user["customchannel"]
# self.married = jav_user["married"]
# self.marriage_request = jav_user["marriage_request"]
# self.marriage_request_sent = jav_user["marriage_request_sent"]
# self.cooldown = jav_user["cooldown"]
self.warnings = self.warns()
# def xp_add(self, amount: int = 1) -> None:
# """Add some amount of XP points
# ### Args:
# * `amount` (int, optional): Amount of XP points to give. Defaults to 1.
# """
# self.xp += amount
# col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { "xp": self.xp } })
# def xp_level_up(self) -> None:
# """Add 1 to the current XP level"""
# xp_diff = int(self.xp - self.xp_next)
# xp_next = int(self.xp_next*configGet("multiplier", "leveling")+configGet("addition", "leveling"))
# self.xp = xp_diff
# self.xp_next = xp_next
# self.level += 1
# col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { "xp": xp_diff } })
# col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { "xp_next": xp_next } })
# col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { "level": self.level } })
# def balance_set(self, amount: int) -> None:
# """Set the balance to amount
# ### Args:
# * `amount` (int): Amount of currency to be set
# """
# self.balance = amount
# col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { "balance": self.balance } })
# def balance_add(self, amount: int) -> None:
# """Add amount to the balance
# ### Args:
# * `amount` (int): Amount to be added
# """
# self.balance_set(self.balance+amount)
# def balance_take(self, amount: int) -> bool:
# """Take amount from the balance
# ### Args:
# * `amount` (int): Amount to be taken
# ### Returns:
# * `bool`: True if successful and False if not
# """
# if self.balance >= amount:
# self.balance_set(self.balance-amount)
# return True
# else:
# return False
# #raise NotEnoughMoneyError()
# def balance_transfer(self, amount: int, destination: Union[Any, int]) -> None:
# """Transfer money to another user
# ### Args:
# * `amount` (int): Amount to be transferred
# * `destination` (Union[Any, int]): Destination user of the transfer (should have attribute "id" or be id itself if int)
# ### Raises:
# * `NotEnoughMoneyError`: Not enough money to perform this transaction
# """
# if self.balance >= amount:
# if isinstance(destination, int):
# destination = HoloUser(destination)
# self.balance_take(amount)
# destination.balance_add(amount) # type: ignore
# else:
# raise NotEnoughMoneyError()
# def salary_get(self) -> int:
# """Get the salary level depending on work_xp
# ### Returns:
# * `int`: Amount of money to be earned
# """
# if self.work_xp >= 100:
# return randint(configGet("min", "work", "level", "4"), configGet("max", "work", "level", "4"))
# elif self.work_xp >= 50:
# return randint(configGet("min", "work", "level", "3"), configGet("max", "work", "level", "3"))
# elif self.work_xp > 10:
# return randint(configGet("min", "work", "level", "2"), configGet("max", "work", "level", "2"))
# else:
# return randint(configGet("min", "work", "level", "1"), configGet("max", "work", "level", "1"))
# def work_xp_add(self) -> None:
# self.set("work_xp", self.work_xp+1)
def warns(self) -> int:
"""Get number of warnings user has
@@ -187,15 +84,6 @@ class HoloUser:
col_warnings.insert_one(document={"user": self.id, "warns": count})
logger.info(f"User {self.id} was warned {count} times due to: {reason}")
# def cooldown_go(self, kind: Literal["work", "daily", "weekly", "monthly", "steal"]) -> None:
# """Set cooldown start of kind now
# ### Args:
# * `kind` (Literal["work", "daily", "weekly", "monthly", "steal"]): Kind of a cooldown
# """
# self.cooldown[kind] = datetime.now(tz=timezone.utc)
# col_users.update_one(filter={"_id": self.db_id}, update={ "$set": { "cooldown": self.cooldown } })
def set(self, key: str, value: Any) -> None:
"""Set attribute data and save it into database
@@ -211,26 +99,46 @@ class HoloUser:
)
logger.info(f"Set attribute {key} of user {self.id} to {value}")
async def is_moderator(
self, member: Union[discord.User, discord.Member, discord.member.Member]
) -> bool:
"""Check if user is moderator or council member
### Args:
* `member` (Union[discord.User, discord.Member, discord.member.Member]): Member object
### Returns:
`bool`: `True` if member is a moderator or member of council and `False` if not
"""
if isinstance(member, discord.User):
return False
moderator_role = await config_get("moderators", "roles")
council_role = await config_get("council", "roles")
for role in member.roles:
if role.id == moderator_role or role.id == council_role:
return True
return False
async def is_council(
self, member: Union[discord.User, discord.Member, discord.member.Member]
) -> bool:
"""Check if user is a member of council
### Args:
* `member` (Union[discord.User, discord.Member, discord.member.Member]): Member object
### Returns:
`bool`: `True` if member is a member of council and `False` if not
"""
if isinstance(member, discord.User):
return False
council_role = await config_get("council", "roles")
for role in member.roles:
if role.id == council_role:
return True
return False
# def purge(self) -> None:
# """Completely remove data from database. Will not remove transactions logs and warnings."""
# col_users.delete_one(filter={"_id": self.db_id})
# self.unauthorize()
# def unauthorize(self) -> None:
# """Cancel Oauth2 authorization"""
# col_authorized.find_one_and_delete({"user": self.id})
# def is_authorized(self) -> bool:
# """Check if user provided Oauth2 authorization
# ### Returns:
# * `bool`: True if yes and False if no
# """
# if configGet("mode") == "secure":
# authorized = col_authorized.find_one({"user": self.id})
# if authorized is not None:
# return True
# else:
# return False
# else:
# return True

View File

@@ -1,23 +1,20 @@
import logging
from datetime import timedelta
from os import getpid, system
from typing import Union
from discord import ApplicationContext, Embed, User, slash_command, option
from discord import ApplicationContext, Embed, User, option, slash_command
from discord import utils as ds_utils
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from classes.holo_user import HoloUser
from enums.colors import Color
from modules.scheduled import scheduler
from modules.utils import config_get
from modules.utils_sync import config_get_sync, guild_name
from modules.scheduled import scheduler
logger = logging.getLogger(__name__)
class Admin(commands.Cog):
def __init__(self, client):
def __init__(self, client: PycordBot):
self.client = client
# @slash_command(
@@ -87,7 +84,7 @@ class Admin(commands.Cog):
# )
# )
# mod_role = ds_utils.get(
# ctx.user.guild.roles, id=await config_get("moderator", "roles")
# ctx.user.guild.roles, id=await config_get("moderators", "roles")
# )
# admin_chan = ds_utils.get(
# ctx.user.guild.channels,
@@ -115,9 +112,12 @@ class Admin(commands.Cog):
amount: int,
user: User,
):
if ctx.user.id in await config_get("admins"):
if ctx.user.id in self.client.owner_ids:
logging.info(
f"User {ctx.user.id} removed {amount} message(s) in {ctx.channel.id}"
"User %s removed %s message(s) in %s",
ctx.user.id,
amount,
ctx.channel.id,
)
await ctx.respond(
embed=Embed(description="Видаляю..."), ephemeral=True, delete_after=2.0
@@ -130,7 +130,8 @@ class Admin(commands.Cog):
)
else:
logging.warning(
f"User {guild_name(ctx.user)} tried to use /clear but permission denied"
"User %s tried to use /clear but permission denied",
guild_name(ctx.user),
)
await ctx.respond(
embed=Embed(
@@ -140,7 +141,7 @@ class Admin(commands.Cog):
)
)
mod_role = ds_utils.get(
ctx.user.guild.roles, id=await config_get("moderator", "roles")
ctx.user.guild.roles, id=await config_get("moderators", "roles")
)
admin_chan = ds_utils.get(
ctx.user.guild.channels,
@@ -162,8 +163,8 @@ class Admin(commands.Cog):
)
async def reboot_cmd(self, ctx: ApplicationContext):
await ctx.defer(ephemeral=True)
if ctx.user.id in await config_get("admins"):
logging.info(f"Calling shutdown initiated by {guild_name(ctx.user)}")
if ctx.user.id in self.client.owner_ids:
logging.info("Calling shutdown initiated by %s", guild_name(ctx.user))
await ctx.respond(
embed=Embed(
title="Вимикаюсь...",
@@ -175,7 +176,8 @@ class Admin(commands.Cog):
exit()
else:
logging.warning(
f"User {guild_name(ctx.user)} tried to use /reboot but permission denied"
"User %s tried to use /reboot but permission denied",
guild_name(ctx.user),
)
await ctx.respond(
embed=Embed(
@@ -185,7 +187,7 @@ class Admin(commands.Cog):
)
)
mod_role = ds_utils.get(
ctx.user.guild.roles, id=await config_get("moderator", "roles")
ctx.user.guild.roles, id=await config_get("moderators", "roles")
)
admin_chan = ds_utils.get(
ctx.user.guild.channels,
@@ -199,3 +201,7 @@ class Admin(commands.Cog):
color=Color.fail,
),
)
def setup(client: PycordBot):
client.add_cog(Admin(client))

View File

@@ -1,17 +1,16 @@
import logging
from discord import (
Cog,
Message,
)
from discord.ext import commands
from modules.database import col_analytics
from discord import Cog, Message
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from modules.database import col_analytics
logger = logging.getLogger(__name__)
class Analytics(commands.Cog):
def __init__(self, client):
def __init__(self, client: PycordBot):
self.client = client
@Cog.listener()
@@ -21,10 +20,42 @@ class Analytics(commands.Cog):
and (message.author.bot == False)
and (message.author.system == False)
):
stickers = []
for sticker in message.stickers:
stickers.append(
{
"id": sticker.id,
"name": sticker.name,
"format": sticker.format,
"url": sticker.url,
}
)
attachments = []
for attachment in message.attachments:
attachments.append(
{
"content_type": attachment.content_type,
"description": attachment.description,
"filename": attachment.filename,
"is_spoiler": attachment.is_spoiler(),
"size": attachment.size,
"url": attachment.url,
"width": attachment.width,
"height": attachment.height,
}
)
col_analytics.insert_one(
{
"message": message.content,
"user": message.author.id,
"channel": message.channel.id,
"content": message.content,
"stickers": stickers,
"attachments": attachments,
}
)
def setup(client: PycordBot):
client.add_cog(Analytics(client))

View File

@@ -1,18 +1,27 @@
from discord import ApplicationContext, Embed, option
from discord import utils as ds_utils
from discord.abc import GuildChannel
from discord.commands import SlashCommandGroup
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from classes.holo_user import HoloUser
from enums.colors import Color
from modules.database import col_users
from modules.utils import config_get
from modules.utils_sync import config_get_sync, guild_name
class CustomChannels(commands.Cog):
def __init__(self, client):
def __init__(self, client: PycordBot):
self.client = client
@commands.Cog.listener()
async def on_guild_channel_delete(self, channel: GuildChannel):
col_users.find_one_and_update(
{"customchannel": channel.id}, {"$set": {"customchannel": None}}
)
customchannel = SlashCommandGroup("customchannel", "Керування особистим каналом")
@customchannel.command(
@@ -72,7 +81,7 @@ class CustomChannels(commands.Cog):
await ctx.respond(
embed=Embed(
title="Помилка виконання",
description=f"У вас вже є особистий канал.\nДля редагування каналу є `/customchannel edit` або просто відкрийте меню керування вашим каналом.",
description="У вас вже є особистий канал.\nДля редагування каналу є `/customchannel edit` або просто відкрийте меню керування вашим каналом.",
color=Color.fail,
)
)
@@ -90,12 +99,14 @@ class CustomChannels(commands.Cog):
):
holo_user_ctx = HoloUser(ctx.user)
custom_channel = ds_utils.get(ctx.guild.channels, id=holo_user_ctx.customchannel)
custom_channel = ds_utils.get(
ctx.guild.channels, id=holo_user_ctx.customchannel
)
if custom_channel is None:
await ctx.respond(
embed=Embed(
title="Канал не знайдено",
description=f"Канал, вказаний як ваш, не існує. Можливо, його було вручну видалено раніше.",
description="Канал, вказаний як ваш, не існує. Можливо, його було вручну видалено раніше.",
color=Color.fail,
)
)
@@ -122,7 +133,9 @@ class CustomChannels(commands.Cog):
guild_ids=[config_get_sync("guild")],
)
@option("confirm", description="Підтвердження операції")
async def customchannel_remove_cmd(self, ctx: ApplicationContext, confirm: bool = False):
async def customchannel_remove_cmd(
self, ctx: ApplicationContext, confirm: bool = False
):
holo_user_ctx = HoloUser(ctx.user)
if holo_user_ctx.customchannel is not None:
@@ -134,7 +147,7 @@ class CustomChannels(commands.Cog):
await ctx.respond(
embed=Embed(
title="Канал не знайдено",
description=f"Канал, вказаний як ваш, не існує. Можливо, його було вручну видалено раніше.",
description="Канал, вказаний як ваш, не існує. Можливо, його було вручну видалено раніше.",
color=Color.fail,
)
)
@@ -144,7 +157,7 @@ class CustomChannels(commands.Cog):
await ctx.respond(
embed=Embed(
title="Підтвердження не надано",
description=f"Для підтвердження операції додайте до команди параметр `confirm` зі значенням `True`.",
description="Для підтвердження операції додайте до команди параметр `confirm` зі значенням `True`.",
color=Color.fail,
)
)
@@ -154,7 +167,7 @@ class CustomChannels(commands.Cog):
await ctx.respond(
embed=Embed(
title="Канал знищено",
description=f"Ви відмовились від каналу та видалили його.",
description="Ви відмовились від каналу та видалили його.",
color=Color.default,
)
)
@@ -163,7 +176,11 @@ class CustomChannels(commands.Cog):
await ctx.respond(
embed=Embed(
title="Помилка виконання",
description=f"У вас немає особистого каналу.",
description="У вас немає особистого каналу.",
color=Color.fail,
)
)
def setup(client: PycordBot):
client.add_cog(CustomChannels(client))

View File

@@ -1,15 +1,17 @@
import logging
from datetime import timedelta
from os import getpid, makedirs, system
from os import makedirs
from pathlib import Path
from uuid import uuid4
from discord import ApplicationContext, Embed, Option, User, slash_command, File
from discord import ApplicationContext, Embed, File, option
from discord import utils as ds_utils
from discord.commands import SlashCommandGroup
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from classes.holo_user import HoloUser
from enums.colors import Color
from modules.database import col_users
from modules.utils import config_get
from modules.utils_sync import config_get_sync, guild_name, json_write_sync
@@ -17,43 +19,53 @@ logger = logging.getLogger(__name__)
class Data(commands.Cog):
def __init__(self, client):
def __init__(self, client: PycordBot):
self.client = client
@slash_command(
data = SlashCommandGroup("data", "Керування даними користувачів")
@data.command(
name="export",
description="Експортувати дані",
guild_ids=[config_get_sync("guild")],
)
async def warn_cmd(
self,
ctx: ApplicationContext,
):
@option(
"kind", description="Тип даних, які треба експортувати", choices=["Користувачі"]
)
async def data_export_cmd(self, ctx: ApplicationContext, kind: str):
await ctx.defer()
if ctx.user.id in await config_get("admins"):
holo_user = HoloUser(ctx.author)
if (ctx.user.id in self.client.owner_ids) or (
await holo_user.is_council(ctx.author)
):
logging.info(
f"Moderator {guild_name(ctx.user)} exported current users list"
"Moderator %s exported current users list", guild_name(ctx.user)
)
makedirs("tmp", exist_ok=True)
uuid = str(uuid4())
users = []
for member in ctx.guild.members:
users.append(
{
"id": member.id,
"nick": member.nick,
"username": f"{member.name}#{member.discriminator}",
"bot": member.bot,
}
if kind == "Користувачі":
users = []
for member in ctx.guild.members:
users.append(
{
"id": member.id,
"nick": member.nick,
"username": f"{member.name}#{member.discriminator}",
"bot": member.bot,
}
)
json_write_sync(users, str(Path(f"tmp/{uuid}")))
await ctx.respond(
file=File(str(Path(f"tmp/{uuid}")), filename="users.json")
)
json_write_sync(users, str(Path(f"tmp/{uuid}")))
await ctx.respond(file=File(str(Path(f"tmp/{uuid}")), filename="users.json"))
else:
logging.info(
f"User {guild_name(ctx.user)} tried to use /export but permission denied"
"User %s tried to use /export but permission denied",
guild_name(ctx.user),
)
await ctx.respond(
embed=Embed(
@@ -63,7 +75,7 @@ class Data(commands.Cog):
)
)
mod_role = ds_utils.get(
ctx.user.guild.roles, id=await config_get("moderator", "roles")
ctx.user.guild.roles, id=await config_get("moderators", "roles")
)
admin_chan = ds_utils.get(
ctx.user.guild.channels,
@@ -77,3 +89,79 @@ class Data(commands.Cog):
color=Color.fail,
),
)
@data.command(
name="migrate",
description="Мігрувати всіх користувачів до бази",
guild_ids=[config_get_sync("guild")],
)
@option(
"kind", description="Тип даних, які треба експортувати", choices=["Користувачі"]
)
async def data_migrate_cmd(self, ctx: ApplicationContext, kind: str):
await ctx.defer()
holo_user = HoloUser(ctx.author)
if (ctx.user.id in self.client.owner_ids) or (
await holo_user.is_council(ctx.author)
):
logging.info(
"Moderator %s started migration of all members to the database",
guild_name(ctx.user),
)
if kind == "Користувачі":
for member in ctx.guild.members:
if member.bot:
continue
if col_users.find_one({"user": member.id}) is None:
user = {}
defaults = await config_get("user", "defaults")
user["user"] = member.id
for key in defaults:
user[key] = defaults[key]
col_users.insert_one(document=user)
logging.info(
"Added DB record for user %s during migration", member.id
)
await ctx.respond(
embed=Embed(
title="Міграцію завершено",
description="Всім користувачам сервера було створено записи в базі даних.",
color=Color.success,
)
)
else:
logging.info(
"User %s tried to use /migrate but permission denied",
guild_name(ctx.user),
)
await ctx.respond(
embed=Embed(
title="Відмовлено в доступі",
description="Здається, це команда лише для модераторів",
color=Color.fail,
)
)
mod_role = ds_utils.get(
ctx.user.guild.roles, id=await config_get("moderators", "roles")
)
admin_chan = ds_utils.get(
ctx.user.guild.channels,
id=await config_get("adminchat", "channels", "text"),
)
await admin_chan.send(
content=f"{mod_role.mention}",
embed=Embed(
title="Неавторизований запит",
description=f"Користувач {ctx.user.mention} запитав у каналі {ctx.channel.mention} команду, до якої не повинен мати доступу/бачити.",
color=Color.fail,
),
)
def setup(client: PycordBot):
client.add_cog(Data(client))

59
cogs/fun.py Normal file
View File

@@ -0,0 +1,59 @@
import logging
from discord import ApplicationContext, Embed, User, option, slash_command
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from WaifuPicsPython import WaifuAsync
from modules.utils import config_get
from modules.utils_sync import config_get_sync, guild_name
logger = logging.getLogger(__name__)
wafiu_pics = WaifuAsync()
class Fun(commands.Cog):
def __init__(self, client: PycordBot):
self.client = client
@slash_command(
name="action",
description="Провести над користувачем РП дію",
guild_ids=[config_get_sync("guild")],
)
@option(
"type",
description="Тип дії, яку хочете провести з користувачем",
choices=config_get_sync("actions").keys(),
)
@option("user", description="Користувач")
async def action_cmd(self, ctx: ApplicationContext, type: str, user: User):
await ctx.defer()
action = await config_get("category", "actions", type)
action_verb = await config_get("action", "actions", type)
image = await wafiu_pics.sfw(action)
logger.info(
"User %s (%s) %s %s (%s) with image %s",
guild_name(ctx.user),
ctx.user.id,
action_verb,
guild_name(user),
user.id,
image,
)
embed = Embed(
description=f"**{guild_name(ctx.user)}** {action_verb} **{guild_name(user)}**",
color=0x2F3136,
)
embed.set_image(url=image)
await ctx.respond(embed=embed)
def setup(client: PycordBot):
client.add_cog(Fun(client))

View File

@@ -1,25 +1,27 @@
from discord import Member, Message
from discord import utils as ds_utils
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from modules.database import col_users
from modules.utils import config_get
class Logger(commands.Cog):
def __init__(self, client):
def __init__(self, client: PycordBot):
self.client = client
@commands.Cog.listener()
async def on_message(self, message: Message):
if (message.author != self.client.user) and (message.author.bot == False) and (message.author.system == False):
if (
(message.author != self.client.user)
and (message.author.bot == False)
and (message.author.system == False)
):
if col_users.find_one({"user": message.author.id}) is None:
user = {}
defaults = await config_get("user", "defaults")
user["user"] = message.author.id
for key in defaults:
@@ -29,21 +31,37 @@ class Logger(commands.Cog):
@commands.Cog.listener()
async def on_member_join(self, member: Member):
welcome_chan = ds_utils.get(
self.client.get_guild(await config_get("guild")).channels,
id=await config_get("welcome", "channels", "text"),
)
rules_chan = ds_utils.get(
self.client.get_guild(await config_get("guild")).channels,
id=await config_get("rules", "channels", "text"),
)
welcome_chan = ds_utils.get(self.client.get_guild(await config_get("guild")).channels, id=await config_get("logging", "channels", "text"))
rules_chan = ds_utils.get(self.client.get_guild(await config_get("guild")).channels, id=await config_get("rules", "channels", "text"))
if (member != self.client.user) and (member.bot == False) and (member.system == False):
await welcome_chan.send(content=(await config_get("welcome", "messages")).format(mention=member.mention, rules=rules_chan.mention))
if (
(member != self.client.user)
and (member.bot == False)
and (member.system == False)
):
await welcome_chan.send(
content=(await config_get("welcome", "messages")).format(
mention=member.mention, rules=rules_chan.mention
)
)
if col_users.find_one({"user": member.id}) is None:
col_users.insert_one(document=user)
user = {}
defaults = await config_get("user", "defaults")
user["user"] = member.id
for key in defaults:
user[key] = defaults[key]
user[key] = defaults[key]
col_users.insert_one(document=user)
def setup(client: PycordBot):
client.add_cog(Logger(client))

View File

@@ -1,9 +1,20 @@
{
"token": "",
"owner": 0,
"locale": "en",
"debug": false,
"guild": 0,
"admins": [],
"status": "crying clowns",
"bot": {
"owners": [
0
],
"debug_guilds": [
0
],
"bot_token": ""
},
"status": {
"type": "playing",
"message": "on your nerves"
},
"database": {
"user": null,
"password": null,
@@ -33,10 +44,45 @@
"voice": {}
},
"roles": {
"moderator": 0
"council": 0,
"moderators": 0
},
"bots": {},
"messages": {
"welcome": "Вітаємо {mention} на сервері HoloUA! Будь ласка, ознайомся з правилами серверу на каналі {rules}. Сподіваємося, тобі тут сподобається!"
},
"actions": {
"Вкусити": {
"category": "bite",
"action": "робить кусь"
},
"Обійняти": {
"category": "hug",
"action": "обіймає"
},
"Поцілувати": {
"category": "kiss",
"action": "цілує"
},
"Лизнути": {
"category": "lick",
"action": "лиже"
},
"Погладити": {
"category": "pat",
"action": "гладить"
},
"Тикнути": {
"category": "poke",
"action": "тикає в"
},
"Помахати": {
"category": "wave",
"action": "махає"
},
"Підморгнути": {
"category": "wink",
"action": "підморгує"
}
}
}

9
locale/en.json Normal file
View File

@@ -0,0 +1,9 @@
{
"metadata": {
"flag": "🇬🇧",
"name": "English",
"codes": [
"en"
]
}
}

9
locale/uk.json Normal file
View File

@@ -0,0 +1,9 @@
{
"metadata": {
"flag": "🇺🇦",
"name": "Українська",
"codes": [
"uk"
]
}
}

54
main.py
View File

@@ -1,19 +1,12 @@
import asyncio
import logging
from discord import Activity, ActivityType
from modules.scheduled import scheduler
from modules.client import client
from modules.scheduled import scheduler
from modules.utils import config_get
from modules.utils_sync import config_get_sync
from cogs.admin import Admin
from cogs.analytics import Analytics
from cogs.custom_channels import CustomChannels
from cogs.data import Data
from cogs.logger import Logger
logging.basicConfig(
level=logging.INFO,
format="%(name)s.%(funcName)s | %(levelname)s | %(message)s",
@@ -32,22 +25,49 @@ except ImportError:
@client.event
async def on_ready():
logger.info(f"Logged in as {client.user}")
await client.change_presence(
activity=Activity(type=ActivityType.listening, name=await config_get("status"))
logger.info("Logged in as %s", client.user)
activity_type = await config_get("type", "status")
activity_message = await config_get("message", "status")
if activity_type == "playing":
await client.change_presence(
activity=Activity(type=ActivityType.playing, name=activity_message)
)
elif activity_type == "watching":
await client.change_presence(
activity=Activity(type=ActivityType.watching, name=activity_message)
)
elif activity_type == "listening":
await client.change_presence(
activity=Activity(type=ActivityType.listening, name=activity_message)
)
elif activity_type == "streaming":
await client.change_presence(
activity=Activity(type=ActivityType.streaming, name=activity_message)
)
elif activity_type == "competing":
await client.change_presence(
activity=Activity(type=ActivityType.competing, name=activity_message)
)
elif activity_type == "custom":
await client.change_presence(
activity=Activity(type=ActivityType.custom, name=activity_message)
)
else:
return
logger.info(
"Set activity type to %s with message %s", activity_type, activity_message
)
def main():
client.add_cog(Admin(client))
client.add_cog(Analytics(client))
client.add_cog(CustomChannels(client))
# client.add_cog(Data(client))
client.add_cog(Logger(client))
client.load_extension("cogs")
try:
scheduler.start()
client.run(config_get_sync("token"))
client.run(config_get_sync("bot_token", "bot"))
except KeyboardInterrupt:
scheduler.shutdown()
exit()

View File

@@ -1,5 +1,6 @@
from discord import Intents, Bot
from discord import Intents
from libbot.pycord.classes import PycordBot
intents = Intents().all()
intents.members = True
client = Bot(intents=intents)
client = PycordBot(intents=intents)

View File

@@ -25,7 +25,3 @@ for collection in ["users", "warnings", "scheduler", "analytics"]:
col_users = db.get_collection("users")
col_warnings = db.get_collection("warnings")
col_analytics = db.get_collection("analytics")
# col_checkouts = db.get_collection("checkouts")
# col_trackings = db.get_collection("trackings")
# col_authorized = db.get_collection("authorized")
# col_transactions = db.get_collection("transactions")

View File

@@ -12,7 +12,9 @@ async def json_read(path: str) -> Any:
async def json_write(data: Any, path: str) -> None:
async with aiofiles.open(path, mode="w", encoding="utf-8") as f:
await f.write(dumps(data, ensure_ascii=False, escape_forward_slashes=False, indent=4))
await f.write(
dumps(data, ensure_ascii=False, escape_forward_slashes=False, indent=4)
)
async def config_get(key: str, *path: str) -> Any:

View File

@@ -1,6 +1,6 @@
from typing import Any
from typing import Any, Union
from discord import Member
from discord import Member, User
from ujson import dumps, loads
@@ -36,7 +36,9 @@ def config_set_sync(key: str, value: Any, *path: str) -> None:
return
def guild_name(member: Member):
def guild_name(member: Union[Member, User]) -> str:
if isinstance(member, User):
return member.name
if member.nick == None:
return member.name
else:

View File

@@ -1,5 +1,8 @@
aiofiles==23.1.0
apscheduler==3.10.1
py-cord[speed]==2.4.1
pymongo==4.3.3
ujson==5.7.0
aiofiles==23.2.1
apscheduler==3.10.4
pymongo~=4.7.3
requests~=2.32.3
ujson~=5.10.0
WaifuPicsPython==0.2.0
--extra-index-url https://git.end-play.xyz/api/packages/profitroll/pypi/simple
libbot[speed,pycord]==3.2.2

54
validation/analytics.json Normal file
View File

@@ -0,0 +1,54 @@
{
"$jsonSchema": {
"required": [
"user",
"channel",
"content",
"stickers",
"attachments"
],
"properties": {
"user": {
"bsonType": "long",
"description": "Discord ID of user"
},
"channel": {
"bsonType": "int",
"description": "Discord ID of a channel"
},
"content": {
"bsonType": ["null", "string"],
"description": "Text of the message"
},
"stickers": {
"bsonType": "array",
"items": {
"bsonType": "object",
"required": [
"id",
"name",
"format",
"url"
],
"properties": {
"id": {
"bsonType": "int"
},
"name": {
"bsonType": "string"
},
"format": {
"bsonType": "array"
},
"user": {
"bsonType": "string"
}
}
}
},
"attachments": {
"bsonType": "array"
}
}
}
}

24
validation/users.json Normal file
View File

@@ -0,0 +1,24 @@
{
"$jsonSchema": {
"required": [
"user",
"customrole",
"customchannel"
],
"properties": {
"user": {
"bsonType": "long",
"description": "Discord ID of user"
},
"customrole": {
"bsonType": ["null", "long"],
"description": "Discord ID of custom role or 'null' if not set"
},
"customchannel": {
"bsonType": ["null", "long"],
"description": "Discord ID of custom channel or 'null' if not set"
}
}
}
}

18
validation/warnings.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$jsonSchema": {
"required": [
"user",
"warns"
],
"properties": {
"user": {
"bsonType": "long",
"description": "Discord ID of user"
},
"warns": {
"bsonType": "int",
"description": "Number of warnings on count"
}
}
}
}