3 Commits

Author SHA1 Message Date
3010dc02bc Merge pull request 'v0.1.0-rc.3' (#15) from dev into main
Reviewed-on: #15
2024-12-16 22:08:22 +02:00
4afcbc93d5 Merge pull request 'Fixed minor issues' (#6) from dev into main
Reviewed-on: #6
2024-06-23 13:14:08 +03: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
16 changed files with 114 additions and 242 deletions

View File

@@ -1,40 +0,0 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.DS_Store
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
config.json
.renovaterc
**/.idea
**/.mypy_cache
validation

View File

@@ -1,27 +0,0 @@
ARG PYTHON_VERSION=3.12.8
FROM python:${PYTHON_VERSION}-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,source=requirements.txt,target=requirements.txt \
python -m pip install -r requirements.txt
USER appuser
COPY . .
ENTRYPOINT ["python", "main.py"]

View File

@@ -7,32 +7,14 @@
<a href="https://git.end-play.xyz/HoloUA/Discord"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
</p>
## Installation from release
## Installation
1. Install MongoDB using the [official installation manual](https://www.mongodb.com/docs/manual/installation)
2. Install Python 3.9+ (3.11+ is recommended)
3. Download the [latest release](https://git.end-play.xyz/HoloUA/Discord/releases/latest)'s archive
4. Extract the archive
5. Navigate to the extracted folder and subfolder `Discord` in it
6. Create a virtual environment:
`python -m venv .venv` or `virtualenv .venv`
7. Activate the virtual environment:
Windows: `.venv\Scripts\activate.bat`
Linux/macOS: `.venv/bin/activate`
8. Install dependencies:
`python -m pip install -r requirements.txt`
9. Run the bot with `python main.py` after completing the [configuration](#Configuration)
## Installation with Git
1. Install MongoDB using the [official installation manual](https://www.mongodb.com/docs/manual/installation)
2. Install Python 3.9+ (3.11+ is recommended)
3. Clone the repository:
`git clone https://git.end-play.xyz/HoloUA/Discord.git`
4. `cd Discord`
5. Install dependencies:
`python -m pip install -r requirements.txt`
6. Run the bot with `python main.py` after completing the [configuration](#Configuration)
1. Install MongoDB using the [official installation manual](https://www.mongodb.com/docs/manual/installation/).
2. `git clone https://git.end-play.xyz/HoloUA/Discord.git`
3. `cd Discord`
4. Install Python 3.9+ (at least 3.11 is recommended) for your OS
5. `python3 -m pip install -r requirements.txt`
6. Run it with `python3 main.py` after configuring
## Configuration
@@ -54,22 +36,3 @@ Mandatory keys to modify:
- roles.*
After all of that you're good to go! Happy using :)
## Docker [Experimental]
As an experiment, Docker deployment option has been added.
### Building the image
1. `git clone https://git.end-play.xyz/HoloUA/Discord.git`
2. `cd Discord`
3. `docker build -t holoua-discord .`
### Starting the bot
1. Install MongoDB using the [official installation manual](https://www.mongodb.com/docs/manual/installation)
2. Download
the [configuration example file](https://git.end-play.xyz/HoloUA/Discord/src/branch/main/config_example.json) and
store it somewhere you would like your bot to access it
3. Complete the [configuration](#Configuration) step for this file
4. `docker run -d -v /path/to/config.json:/app/config.json holoua-discord`

View File

@@ -1,6 +0,0 @@
from libbot.pycord.classes import PycordBot
class HoloBot(PycordBot):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View File

@@ -3,7 +3,7 @@ from typing import Any, Union, Dict
from bson import ObjectId
from discord import User, Member
from libbot.utils import config_get
from libbot import config_get
from errors import UserNotFoundError
from modules.database import col_warnings, sync_col_users, sync_col_warnings, col_users

View File

@@ -13,9 +13,10 @@ from discord import (
)
from discord import utils as ds_utils
from discord.ext import commands
from libbot.utils import config_get
from libbot import config_get
from libbot.pycord.classes import PycordBot
from libbot.sync import config_get as sync_config_get
from classes.holo_bot import HoloBot
from enums import Color
from modules.scheduler import scheduler
from modules.utils_sync import guild_name
@@ -27,8 +28,8 @@ logger = logging.getLogger(__name__)
class Admin(commands.Cog):
"""Cog with utility commands for admins."""
def __init__(self, client: HoloBot):
self.client: HoloBot = client
def __init__(self, client: PycordBot):
self.client: PycordBot = client
# Disabled because warning functionality is temporarily not needed
# @slash_command(
@@ -116,7 +117,7 @@ class Admin(commands.Cog):
@slash_command(
name="clear",
description="Видалити деяку кількість повідомлень в каналі",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option("amount", description="Кількість")
@option("user", description="Користувач", default=None)
@@ -181,7 +182,7 @@ class Admin(commands.Cog):
@slash_command(
name="reboot",
description="Перезапустити бота",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
async def reboot_cmd(self, ctx: ApplicationContext) -> None:
await ctx.defer(ephemeral=True)
@@ -235,5 +236,5 @@ class Admin(commands.Cog):
)
def setup(client: HoloBot) -> None:
def setup(client: PycordBot) -> None:
client.add_cog(Admin(client))

View File

@@ -3,16 +3,16 @@ from typing import Dict, List, Any
from discord import Cog, Message
from discord.ext import commands
from libbot.pycord.classes import PycordBot
from classes.holo_bot import HoloBot
from modules.database import col_analytics
logger = logging.getLogger(__name__)
class Analytics(commands.Cog):
def __init__(self, client: HoloBot):
self.client: HoloBot = client
def __init__(self, client: PycordBot):
self.client: PycordBot = client
@Cog.listener()
async def on_message(self, message: Message) -> None:
@@ -60,5 +60,5 @@ class Analytics(commands.Cog):
)
def setup(client: HoloBot) -> None:
def setup(client: PycordBot) -> None:
client.add_cog(Analytics(client))

View File

@@ -6,9 +6,10 @@ from discord import utils as ds_utils
from discord.abc import GuildChannel
from discord.commands import SlashCommandGroup
from discord.ext import commands
from libbot.utils import config_get
from libbot import config_get
from libbot.pycord.classes import PycordBot
from libbot.sync import config_get as sync_config_get
from classes.holo_bot import HoloBot
from classes.holo_user import HoloUser
from enums import Color
from modules.database import col_users
@@ -18,8 +19,8 @@ logger = logging.getLogger(__name__)
class CustomChannels(commands.Cog):
def __init__(self, client: HoloBot):
self.client: HoloBot = client
def __init__(self, client: PycordBot):
self.client: PycordBot = client
@commands.Cog.listener()
async def on_guild_channel_delete(self, channel: GuildChannel) -> None:
@@ -34,7 +35,7 @@ class CustomChannels(commands.Cog):
@custom_channel_group.command(
name="get",
description="Отримати персональний текстовий канал",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option("name", description="Назва каналу")
@option("reactions", description="Дозволити реакції")
@@ -121,7 +122,7 @@ class CustomChannels(commands.Cog):
@custom_channel_group.command(
name="edit",
description="Змінити параметри особистого каналу",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option("name", description="Назва каналу")
@option("reactions", description="Дозволити реакції")
@@ -166,7 +167,7 @@ class CustomChannels(commands.Cog):
@custom_channel_group.command(
name="remove",
description="Відібрати канал, знищуючи його, та частково повернути кошти",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option("confirm", description="Підтвердження операції")
async def custom_channel_remove_cmd(
@@ -233,5 +234,5 @@ class CustomChannels(commands.Cog):
)
def setup(client: HoloBot) -> None:
def setup(client: PycordBot) -> None:
client.add_cog(CustomChannels(client))

View File

@@ -1,5 +1,4 @@
import logging
from logging import Logger
from os import makedirs
from pathlib import Path
from typing import Union, List, Dict, Any
@@ -9,27 +8,29 @@ from discord import ApplicationContext, Embed, File, option, Role, TextChannel
from discord import utils as ds_utils
from discord.commands import SlashCommandGroup
from discord.ext import commands
from libbot.utils import config_get, json_write
from libbot import config_get
from libbot.pycord.classes import PycordBot
from libbot.sync import config_get as sync_config_get
from libbot.sync import json_write as sync_json_write
from classes.holo_bot import HoloBot
from classes.holo_user import HoloUser
from enums import Color
from modules.database import col_users
from modules.utils_sync import guild_name
logger: Logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
class Data(commands.Cog):
def __init__(self, client: HoloBot):
self.client: HoloBot = client
def __init__(self, client: PycordBot):
self.client: PycordBot = client
data: SlashCommandGroup = SlashCommandGroup("data", "Керування даними користувачів")
@data.command(
name="export",
description="Експортувати дані",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option(
"kind", description="Тип даних, які треба експортувати", choices=["Користувачі"]
@@ -92,16 +93,14 @@ class Data(commands.Cog):
}
)
# Temporary file must be written synchronously,
# otherwise it will not be there when ctx.respond() is be called
json_write(users, Path(f"tmp/{uuid}"))
sync_json_write(users, Path(f"tmp/{uuid}"))
await ctx.respond(file=File(Path(f"tmp/{uuid}"), filename="users.json"))
@data.command(
name="migrate",
description="Мігрувати всіх користувачів до бази",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option(
"kind", description="Тип даних, які треба експортувати", choices=["Користувачі"]
@@ -180,5 +179,5 @@ class Data(commands.Cog):
)
def setup(client: HoloBot) -> None:
def setup(client: PycordBot) -> None:
client.add_cog(Data(client))

View File

@@ -2,9 +2,10 @@ import logging
from discord import ApplicationContext, Embed, User, option, slash_command
from discord.ext import commands
from libbot.utils import config_get
from libbot import config_get
from libbot.pycord.classes import PycordBot
from libbot.sync import config_get as sync_config_get
from classes.holo_bot import HoloBot
from modules.utils_sync import guild_name
from modules.waifu_pics import waifu_pics
@@ -12,18 +13,18 @@ logger = logging.getLogger(__name__)
class Fun(commands.Cog):
def __init__(self, client: HoloBot):
self.client: HoloBot = client
def __init__(self, client: PycordBot):
self.client: PycordBot = client
@slash_command(
name="action",
description="Провести над користувачем РП дію",
guild_ids=[config_get("guild")],
guild_ids=[sync_config_get("guild")],
)
@option(
"type",
description="Тип дії, яку хочете провести з користувачем",
choices=config_get("actions").keys(),
choices=sync_config_get("actions").keys(),
)
@option("user", description="Користувач")
async def action_cmd(self, ctx: ApplicationContext, type: str, user: User) -> None:
@@ -53,5 +54,5 @@ class Fun(commands.Cog):
await ctx.respond(embed=embed)
def setup(client: HoloBot) -> None:
def setup(client: PycordBot) -> None:
client.add_cog(Fun(client))

View File

@@ -3,15 +3,15 @@ from typing import Dict, Any, Union
from discord import Member, Message, TextChannel
from discord import utils as ds_utils
from discord.ext import commands
from libbot.utils import config_get
from libbot import config_get
from libbot.pycord.classes import PycordBot
from classes.holo_bot import HoloBot
from modules.database import col_users
class Logger(commands.Cog):
def __init__(self, client: HoloBot):
self.client: HoloBot = client
def __init__(self, client: PycordBot):
self.client: PycordBot = client
@commands.Cog.listener()
async def on_message(self, message: Message):
@@ -65,5 +65,5 @@ class Logger(commands.Cog):
await col_users.insert_one(document=user)
def setup(client: HoloBot) -> None:
def setup(client: PycordBot) -> None:
client.add_cog(Logger(client))

View File

@@ -1,57 +0,0 @@
import logging
from logging import Logger
from discord import Activity, ActivityType
from discord.ext import commands
from libbot.utils import config_get
from classes.holo_bot import HoloBot
logger: Logger = logging.getLogger(__name__)
class Utility(commands.Cog):
def __init__(self, client: HoloBot):
self.client: HoloBot = client
@commands.Cog.listener()
async def on_ready(self) -> None:
logger.info("Logged in as %s", self.client.user)
activity_type: str = await config_get("type", "status")
activity_message: str = await config_get("message", "status")
if activity_type == "playing":
await self.client.change_presence(
activity=Activity(type=ActivityType.playing, name=activity_message)
)
elif activity_type == "watching":
await self.client.change_presence(
activity=Activity(type=ActivityType.watching, name=activity_message)
)
elif activity_type == "listening":
await self.client.change_presence(
activity=Activity(type=ActivityType.listening, name=activity_message)
)
elif activity_type == "streaming":
await self.client.change_presence(
activity=Activity(type=ActivityType.streaming, name=activity_message)
)
elif activity_type == "competing":
await self.client.change_presence(
activity=Activity(type=ActivityType.competing, name=activity_message)
)
elif activity_type == "custom":
await self.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 setup(client: HoloBot) -> None:
client.add_cog(Utility(client))

60
main.py
View File

@@ -1,12 +1,12 @@
import logging
import sys
from logging import Logger
from pathlib import Path
from discord import LoginFailure, Intents
from libbot.utils import config_get
from discord import Activity, ActivityType
from libbot import config_get
from libbot.sync import config_get as sync_config_get
from classes.holo_bot import HoloBot
from modules.client import client
from modules.scheduler import scheduler
logging.basicConfig(
@@ -25,25 +25,53 @@ except ImportError:
pass
def main() -> None:
if not Path("config.json").exists():
logger.error(
"Config file is missing: Make sure the configuration file 'config.json' is in place."
@client.event
async def on_ready() -> None:
logger.info("Logged in as %s", client.user)
activity_type: str = await config_get("type", "status")
activity_message: str = await config_get("message", "status")
if activity_type == "playing":
await client.change_presence(
activity=Activity(type=ActivityType.playing, name=activity_message)
)
sys.exit()
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
intents: Intents = Intents().all()
client: HoloBot = HoloBot(intents=intents, scheduler=scheduler)
logger.info(
"Set activity type to %s with message %s", activity_type, activity_message
)
def main() -> None:
client.load_extension("cogs")
try:
client.run(config_get("bot_token", "bot"))
except LoginFailure as exc:
logger.error("Provided bot token is invalid: %s", exc)
scheduler.start()
client.run(sync_config_get("bot_token", "bot"))
except KeyboardInterrupt:
logger.info("KeyboardInterrupt received: Shutting down gracefully.")
finally:
scheduler.shutdown()
sys.exit()

10
modules/client.py Normal file
View File

@@ -0,0 +1,10 @@
from discord import Intents
from libbot.pycord.classes import PycordBot
from modules.scheduler import scheduler
intents: Intents = Intents().all()
intents.members = True
client: PycordBot = PycordBot(intents=intents, scheduler=scheduler)

View File

@@ -1,12 +1,12 @@
from typing import Dict, Any
from async_pymongo import AsyncClient, AsyncCollection, AsyncDatabase
from libbot.utils import config_get
from libbot.sync import config_get as sync_config_get
from pymongo import MongoClient
from pymongo.synchronous.collection import Collection
from pymongo.synchronous.database import Database
db_config: Dict[str, Any] = config_get("database")
db_config: Dict[str, Any] = sync_config_get("database")
con_string: str = (
"mongodb://{0}:{1}/{2}".format(

View File

@@ -5,7 +5,6 @@ requests>=2.32.2
aiofiles~=24.1.0
apscheduler>=3.10.0
async_pymongo==0.1.11
libbot[speed,pycord]==4.0.0
typing-extensions~=4.12.2
libbot[speed,pycord]==3.2.3
ujson~=5.10.0
WaifuPicsPython==0.2.0