-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
1,106 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
name: "CodeQL" | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
pull_request: | ||
branches: | ||
- main | ||
schedule: | ||
- cron: '36 7 * * 0' | ||
|
||
jobs: | ||
Analyze: | ||
name: Analyze | ||
runs-on: ubuntu-latest | ||
permissions: | ||
actions: read | ||
contents: read | ||
security-events: write | ||
|
||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v4 | ||
- name: Set up Python 3.11 | ||
id: setup-python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.11' | ||
- name: Set up Poetry | ||
uses: Gr1N/setup-poetry@v8 | ||
- name: Cache Poetry | ||
id: cache-poetry | ||
uses: actions/cache@v3 | ||
with: | ||
path: ~/.cache/pypoetry/virtualenvs | ||
key: ${{ runner.os }}-codeql-python-${{ hashFiles('**/poetry.lock') }} | ||
- name: Install Poetry Dependencies | ||
if: steps.cache-poetry.outputs.cache-hit != 'true' | ||
run: | | ||
poetry install | ||
- name: Initialize CodeQL | ||
uses: github/codeql-action/init@v2 | ||
with: | ||
languages: python | ||
setup-python-dependencies: false | ||
- name: Perform CodeQL Analysis | ||
uses: github/codeql-action/analyze@v2 | ||
with: | ||
upload: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
name: Lint | ||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
pull_request: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
Analyze: | ||
runs-on: ubuntu-latest | ||
|
||
strategy: | ||
fail-fast: false | ||
matrix: | ||
version: [3.9, '3.10', '3.11'] | ||
|
||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Python ${{ matrix.version }} | ||
id: setup-python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.version }} | ||
|
||
- name: Set up Poetry | ||
uses: Gr1N/setup-poetry@v8 | ||
|
||
- name: Cache Poetry | ||
id: cache-poetry | ||
uses: actions/cache@v3 | ||
with: | ||
path: ~/.cache/pypoetry/virtualenvs | ||
key: ${{ runner.os }}-poetry-lint-${{ matrix.version }}-${{ hashFiles('**/poetry.lock') }} | ||
|
||
- name: Install Poetry Dependencies | ||
if: steps.cache-poetry.outputs.cache-hit != 'true' | ||
run: | | ||
poetry install --with dev | ||
- name: Run Pyright | ||
run: | | ||
poetry run pyright bot | ||
- name: Run Ruff | ||
run: | | ||
poetry run ruff bot |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
name: Release | ||
on: | ||
push: | ||
branches: | ||
- main | ||
jobs: | ||
Release: | ||
runs-on: ubuntu-latest | ||
if: contains(github.event.head_commit.message, '#major') || contains(github.event.head_commit.message, '#minor') || contains(github.event.head_commit.message, '#patch') | ||
steps: | ||
- uses: actions/checkout@v4 | ||
with: | ||
fetch-depth: '0' | ||
|
||
- name: Bump version and push tag | ||
uses: anothrNick/[email protected] | ||
id: tag_version | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
WITH_V: true | ||
RELEASE_BRANCHES: main | ||
|
||
- name: Release New Version | ||
uses: ncipollo/release-action@v1 | ||
with: | ||
bodyFile: "changelog.md" | ||
token: ${{ secrets.PAT_TOKEN }} | ||
tag: ${{ steps.tag_version.outputs.new_tag }} | ||
name: ${{ steps.tag_version.outputs.new_tag }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,62 @@ | ||
# rodhaj | ||
proto-repo for discord app | ||
|
||
**later down the road, pls do not push to main branch directly** | ||
|
||
## Stuff that needs to be done | ||
|
||
- [x] Paginators | ||
- [ ] R. Danny migrations or asyncpg-trek | ||
- [ ] The features | ||
|
||
## Getting Started | ||
|
||
### Preface on Slash Commands | ||
|
||
Unlike other frameworks, discord.py does not automatically sync slash commands (if you want to learn more why, see [this and why Noelle is heavily against it](https://github.com/No767/Zoee#preface-on-slash-commands-and-syncing)). So the way to sync is by using an prefixed commands, which is [Umbra's Sync Command](https://about.abstractumbra.dev/discord.py/2023/01/29/sync-command-example.html). More than likely you'll need to read up on how this slash command works in order to get started. In short, you'll probably want to sync your test bot to the guild instead (as demostrated here): | ||
|
||
``` | ||
# Replace 1235 with your guild id | ||
r>sync 1235 | ||
``` | ||
|
||
|
||
### Setup Instructions | ||
|
||
You must have these installed: | ||
|
||
- Poetry | ||
- Python | ||
- Git | ||
- PostgreSQL | ||
|
||
In order to run pg in a docker container, spin up the docker compose file | ||
located in the root of the repo (`sudo docker compose up -d`). | ||
|
||
1. Clone the repo or use it as a template. | ||
2. Copy over the ENV file template to the `bot` directory | ||
|
||
```bash | ||
cp envs/dev.env bot/.env | ||
``` | ||
3. Install the dependencies | ||
|
||
```bash | ||
poetry install | ||
``` | ||
|
||
4. Configure the settings in the ENV (note that configuring the postgres uri is required) | ||
|
||
5. Run the bot | ||
|
||
```bash | ||
poetry run python bot/launcher.py | ||
``` | ||
|
||
6. Once your bot is running, sync the commands to your guild. You might have to wait a while because the syncing process usually takes some time. Once completed, you should now have the `CommandTree` synced to that guild. | ||
|
||
``` | ||
# Replace 12345 with your guild id | ||
r>sync 12345 | ||
``` | ||
7. Now go ahead and play around with the default commands. Add your own, delete some, do whatever you want now. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from pkgutil import iter_modules | ||
from typing import Literal, NamedTuple | ||
|
||
|
||
class VersionInfo(NamedTuple): | ||
major: int | ||
minor: int | ||
micro: int | ||
releaselevel: Literal["alpha", "beta", "final"] | ||
|
||
def __str__(self) -> str: | ||
return f"{self.major}.{self.minor}.{self.micro}-{self.releaselevel}" | ||
|
||
|
||
EXTENSIONS = [module.name for module in iter_modules(__path__, f"{__package__}.")] | ||
VERSION: VersionInfo = VersionInfo(major=0, minor=1, micro=0, releaselevel="alpha") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from typing import Literal, Optional | ||
|
||
import discord | ||
from cogs import EXTENSIONS | ||
from discord.ext import commands | ||
from discord.ext.commands import Context, Greedy | ||
|
||
from rodhaj import Rodhaj | ||
|
||
|
||
class DevTools(commands.Cog, command_attrs=dict(hidden=True)): | ||
"""Tools for developing RodHaj""" | ||
|
||
def __init__(self, bot: Rodhaj): | ||
self.bot = bot | ||
|
||
# Umbra's sync command | ||
# To learn more about it, see the link below (and ?tag ass on the dpy server): | ||
# https://about.abstractumbra.dev/discord.py/2023/01/29/sync-command-example.html | ||
@commands.guild_only() | ||
@commands.is_owner() | ||
@commands.command(name="sync", hidden=True) | ||
async def sync( | ||
self, | ||
ctx: Context, | ||
guilds: Greedy[discord.Object], | ||
spec: Optional[Literal["~", "*", "^"]] = None, | ||
) -> None: | ||
"""Performs a sync of the tree. This will sync, copy globally, or clear the tree. | ||
Args: | ||
ctx (Context): Context of the command | ||
guilds (Greedy[discord.Object]): Which guilds to sync to. Greedily accepts a number of guilds | ||
spec (Optional[Literal["~", "*", "^"], optional): Specs to sync. | ||
""" | ||
await ctx.defer() | ||
if not guilds: | ||
if spec == "~": | ||
synced = await self.bot.tree.sync(guild=ctx.guild) | ||
elif spec == "*": | ||
self.bot.tree.copy_global_to(guild=ctx.guild) # type: ignore | ||
synced = await self.bot.tree.sync(guild=ctx.guild) | ||
elif spec == "^": | ||
self.bot.tree.clear_commands(guild=ctx.guild) | ||
await self.bot.tree.sync(guild=ctx.guild) | ||
synced = [] | ||
else: | ||
synced = await self.bot.tree.sync() | ||
|
||
await ctx.send( | ||
f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}" | ||
) | ||
return | ||
|
||
ret = 0 | ||
for guild in guilds: | ||
try: | ||
await self.bot.tree.sync(guild=guild) | ||
except discord.HTTPException: | ||
pass | ||
else: | ||
ret += 1 | ||
|
||
await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.") | ||
|
||
@commands.guild_only() | ||
@commands.is_owner() | ||
@commands.command(name="reload-all", hidden=True) | ||
async def reload_all(self, ctx: commands.Context) -> None: | ||
"""Reloads all cogs. Used in production to not produce any downtime""" | ||
if not hasattr(self.bot, "uptime"): | ||
await ctx.send("Bot + exts must be up and loaded before doing this") | ||
return | ||
|
||
for extension in EXTENSIONS: | ||
await self.bot.reload_extension(extension) | ||
await ctx.send("Successfully reloaded all extensions live") | ||
|
||
|
||
async def setup(bot: Rodhaj): | ||
await bot.add_cog(DevTools(bot)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import datetime | ||
import itertools | ||
import platform | ||
|
||
import discord | ||
import psutil | ||
import pygit2 | ||
from discord import app_commands | ||
from discord.ext import commands | ||
from discord.utils import format_dt | ||
from libs.utils import Embed, human_timedelta | ||
|
||
from rodhaj import Rodhaj | ||
|
||
|
||
# A cog houses a category of commands | ||
# Unlike djs, think of commands being stored as a category, | ||
# which the cog is that category | ||
class Meta(commands.Cog): | ||
def __init__(self, bot: Rodhaj) -> None: | ||
self.bot = bot | ||
self.process = psutil.Process() | ||
|
||
def get_bot_uptime(self, *, brief: bool = False) -> str: | ||
return human_timedelta( | ||
self.bot.uptime, accuracy=None, brief=brief, suffix=False | ||
) | ||
|
||
def format_commit(self, commit: pygit2.Commit) -> str: | ||
short, _, _ = commit.message.partition("\n") | ||
short_sha2 = commit.hex[0:6] | ||
commit_tz = datetime.timezone( | ||
datetime.timedelta(minutes=commit.commit_time_offset) | ||
) | ||
commit_time = datetime.datetime.fromtimestamp(commit.commit_time).astimezone( | ||
commit_tz | ||
) | ||
|
||
# [`hash`](url) message (offset) | ||
offset = format_dt(commit_time.astimezone(datetime.timezone.utc), "R") | ||
return f"[`{short_sha2}`](https://github.com/transprogrammer/rodhaj/commit/{commit.hex}) {short} ({offset})" | ||
|
||
def get_last_commits(self, count: int = 5): | ||
repo = pygit2.Repository(".git") | ||
commits = list( | ||
itertools.islice( | ||
repo.walk(repo.head.target, pygit2.GIT_SORT_TOPOLOGICAL), count | ||
) | ||
) | ||
return "\n".join(self.format_commit(c) for c in commits) | ||
|
||
@app_commands.command(name="about") | ||
async def about(self, interaction: discord.Interaction) -> None: | ||
"""Shows some stats for Rodhaj""" | ||
total_members = 0 | ||
total_unique = len(self.bot.users) | ||
|
||
for guild in self.bot.guilds: | ||
total_members += guild.member_count or 0 | ||
|
||
# For Kumiko, it's done differently | ||
# R. Danny's way of doing it is probably close enough anyways | ||
memory_usage = self.process.memory_full_info().uss / 1024**2 | ||
cpu_usage = self.process.cpu_percent() / psutil.cpu_count() | ||
|
||
revisions = self.get_last_commits() | ||
embed = Embed() | ||
embed.set_author(name=self.bot.user.name, icon_url=self.bot.user.display_avatar.url) # type: ignore | ||
embed.title = "About Me" | ||
embed.description = f"Latest Changes:\n {revisions}" | ||
embed.set_footer( | ||
text=f"Made with discord.py v{discord.__version__}", | ||
icon_url="https://cdn.discordapp.com/emojis/596577034537402378.png?size=128", | ||
) | ||
embed.add_field(name="Servers Count", value=len(self.bot.guilds)) | ||
embed.add_field( | ||
name="User Count", value=f"{total_members} total\n{total_unique} unique" | ||
) | ||
embed.add_field( | ||
name="Process", value=f"{memory_usage:.2f} MiB\n{cpu_usage:.2f}% CPU" | ||
) | ||
embed.add_field(name="Python Version", value=platform.python_version()) | ||
embed.add_field(name="Version", value=str(self.bot.version)) | ||
embed.add_field(name="Uptime", value=self.get_bot_uptime(brief=True)) | ||
await interaction.response.send_message(embed=embed) | ||
|
||
@app_commands.command(name="uptime") | ||
async def uptime(self, interaction: discord.Interaction) -> None: | ||
"""Displays the bot's uptime""" | ||
uptime_message = f"Uptime: {self.get_bot_uptime()}" | ||
await interaction.response.send_message(uptime_message) | ||
|
||
@app_commands.command(name="version") | ||
async def version(self, interaction: discord.Interaction) -> None: | ||
"""Displays the current build version""" | ||
version_message = f"Version: {self.bot.version}" | ||
await interaction.response.send_message(version_message) | ||
|
||
|
||
async def setup(bot: Rodhaj) -> None: | ||
await bot.add_cog(Meta(bot)) |
Oops, something went wrong.