Skip to content

Commit

Permalink
Merge pull request #294 from SylteA/dev
Browse files Browse the repository at this point in the history
Merge into master
  • Loading branch information
SylteA authored Mar 4, 2024
2 parents 5fbf8b2 + 739ef81 commit cce3472
Show file tree
Hide file tree
Showing 26 changed files with 1,443 additions and 776 deletions.
141 changes: 141 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
local.env
.env
.venv
.env.save
env/
venv/
ENV/
env.bak/
venv.bak/
.env.local
.env.staging

# VSCode project settings
.vscode/

# Spyder project settings
.spyderproject
.spyproject

# Pycharm project settings
.idea

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

postgres/
25 changes: 0 additions & 25 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,6 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3

- name: Cache Poetry cache
uses: actions/cache@v2
with:
path: |
~/.cache/pypoetry
key: "poetry-cache-${{ runner.os }}-\
${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}"

- name: Cache Pre-commit cache
uses: actions/cache@v2
with:
path: ~/.cache/pre-commit
key: "pre-commit-cache-${{ runner.os }}-${{ env.PYTHON_VERSION }}-\
${{ env.PRECOMMIT_VERSION }}-\
${{ hashFiles('**/.pre-commit-config.yaml') }}"

# virtualenv cache should depend on OS, Python version and `poetry.lock` (and optionally workflow files).
- name: Cache Packages
uses: actions/cache@v2
with:
path: ~/.local
key: "poetry-${{ runner.os }}-${{ env.PYTHON_VERSION }}-\
${{ hashFiles('**/poetry.lock') }}-\
${{ hashFiles('.github/workflows/*.yaml') }}"

- name: Set up Python
uses: actions/setup-python@v2
with:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
# Contributing

If you're interested in contributing, please review the [Contributing](CONTRIBUTING.md) section. For specific tasks and opportunities, explore our list of open [issues](https://github.com/SylteA/Discord-Bot/issues).
Please reach out in discord if you're interested in contributing or need any help!
6 changes: 0 additions & 6 deletions bot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ class ErrorHandling(BaseModel):
webhook_url: str


class CustomRoles(BaseModel):
log_channel_id: int
divider_role_id: int


class YouTube(BaseModel):
channel_id: str
text_channel_id: int
Expand All @@ -109,7 +104,6 @@ class Settings(BaseSettings):
tags: Tags
hastebin: Hastebin
errors: ErrorHandling
custom_roles: CustomRoles
youtube: YouTube

class Config:
Expand Down
34 changes: 33 additions & 1 deletion bot/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
from discord.ext import commands, tasks

from bot.config import settings
from bot.models import GuildConfig
from bot.services import http, paste
from bot.services.paste import Document
from utils.errors import IgnorableException
from utils.health import update_health
from utils.time import human_timedelta

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -52,6 +54,8 @@ async def setup_hook(self) -> None:
self.loop.create_task(self.when_online())
self.presence.start()

update_health("running", True)

self.tree.on_error = self.on_app_command_error

self.error_webhook = discord.Webhook.from_url(url=settings.errors.webhook_url, session=http.session)
Expand Down Expand Up @@ -79,6 +83,34 @@ async def sync_commands(self) -> None:
log.info("Commands synced.")
log.info(f"Successfully logged in as {self.user}. In {len(self.guilds)} guilds")

@staticmethod
async def on_disconnect():
"""Called when the"""
update_health("ready", False)

async def on_ready(self):
"""Called when all guilds are cached."""
update_health("ready", True)

query = "SELECT COALESCE(array_agg(guild_id), '{}') FROM guild_configs"

stored_ids = await GuildConfig.fetchval(query)
missing_ids = [(guild.id,) for guild in self.guilds if guild.id not in stored_ids]

if missing_ids:
query = "INSERT INTO guild_configs (guild_id) VALUES ($1)"
await GuildConfig.pool.executemany(query, missing_ids)
log.info(f"Inserted new config for {len(missing_ids)} guilds.")

async def on_guild_join(self, guild: discord.Guild):
log.info(f"{self.user.name} has been added to a new guild: {guild.name}")

query = """INSERT INTO guild_configs (guild_id)
VALUES ($1)
ON CONFLICT (guild_id)
DO NOTHING"""
await GuildConfig.execute(query, guild.id)

async def on_message(self, message):
await self.wait_until_ready()

Expand Down Expand Up @@ -118,7 +150,7 @@ def wrap(code: str) -> str:
await self.error_webhook.send(embed=embed)

async def on_error(self, event_method: str, *args: Any, **kwargs: Any) -> None:
content = "\n".join(traceback.format_exception(*sys.exc_info()))
content = "".join(traceback.format_exception(*sys.exc_info()))
header = f"Ignored exception in event method **{event_method}**"

await self.send_error(content, header)
Expand Down
93 changes: 87 additions & 6 deletions bot/extensions/custom_roles/commands.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
from __future__ import annotations

import datetime

import discord
from discord import app_commands, utils
from discord.ext import commands

from bot import core
from bot.config import settings
from bot.models import CustomRole
from bot.models import CustomRole, GuildConfig


class CustomRoles(commands.Cog):
custom_roles = app_commands.Group(
name="custom_roles",
description="Custom Role commands",
default_permissions=discord.Permissions(administrator=True),
)

config = app_commands.Group(
parent=custom_roles,
name="config",
description="Set configuration for custom role",
default_permissions=discord.Permissions(administrator=True),
)

def __init__(self, bot):
self.bot = bot

Expand Down Expand Up @@ -45,13 +59,24 @@ async def myrole(

if before is None:
if name is None:
return await interaction.response.send_message("You don't have a custom role yet!", ephemeral=True)
return await interaction.response.send_message(
"You don't have a custom role yet, specify a name to create one!", ephemeral=True
)

await interaction.response.defer(thinking=True, ephemeral=True)

# Create and assign the role to user
role = await interaction.guild.create_role(name=name, colour=color or discord.Color.random())

divider_role = interaction.guild.get_role(settings.custom_roles.divider_role_id)
await role.edit(position=divider_role.position + 1)
divider_role_query = """
SELECT divider_role_id
FROM guild_configs
WHERE guild_id = $1"""
divider_role_id = await GuildConfig.fetchval(divider_role_query, interaction.guild.id)

if divider_role_id is not None:
divider_role = interaction.guild.get_role(divider_role_id)
await role.edit(position=divider_role.position + 1)

record = await CustomRole.ensure_exists(
guild_id=interaction.guild.id,
Expand All @@ -69,7 +94,7 @@ async def myrole(
role_ids=[role.id],
)

return await interaction.response.send_message(
return await interaction.followup.send(
embed=self.role_embed("**Custom Role has been assigned**", role),
ephemeral=True,
)
Expand Down Expand Up @@ -105,6 +130,62 @@ async def myrole(
ephemeral=True,
)

@config.command(name="log-channel")
@app_commands.describe(channel="New channel")
async def log_channel(self, interaction: discord.Interaction, channel: discord.TextChannel = None):
"""Set custom role log channel"""
query = """
UPDATE guild_configs
SET custom_role_log_channel_id = $1
WHERE guild_id = $2
"""

if channel is None:
await GuildConfig.execute(query, None, interaction.guild.id)
return await interaction.response.send_message("Cleared log channel selection", ephemeral=True)

await GuildConfig.execute(query, channel.id, interaction.guild.id)
return await interaction.response.send_message(f"Log channel set to {channel.mention}", ephemeral=True)

@config.command(name="divider-role-id")
@app_commands.describe(role="divider role")
async def divider_role(self, interaction: discord.Interaction, role: discord.Role = None):
"""Set Divider role"""
query = """
UPDATE guild_configs
SET divider_role_id = $1
WHERE guild_id = $2
"""

if role is None:
await GuildConfig.execute(query, None, interaction.guild.id)
return await interaction.response.send_message("Cleared divider role selection", ephemeral=True)

await GuildConfig.execute(query, role.id, interaction.guild.id)
return await interaction.response.send_message(f"Divider role set to {role.mention}")

@config.command(name="show")
async def show(self, interaction: discord.Interaction):
"""Return the current custom role configuration"""
query = """
SELECT *
FROM guild_configs
WHERE guild_id = $1
"""
data = await GuildConfig.fetchrow(query, interaction.guild.id)

embed = discord.Embed(
title=f"Server Information - {interaction.guild.name}",
description="Custom Role Configuration",
color=discord.Color.blue(),
timestamp=datetime.datetime.utcnow(),
)

embed.set_thumbnail(url=interaction.guild.icon.url)
embed.add_field(name="Log channel", value=f"<#{data.custom_role_log_channel_id}>")

return await interaction.response.send_message(embed=embed)


async def setup(bot: commands.Bot):
await bot.add_cog(CustomRoles(bot=bot))
Loading

0 comments on commit cce3472

Please sign in to comment.