├── .python-version ├── docs ├── content │ ├── user │ │ ├── modules │ │ │ ├── fun.md │ │ │ ├── info.md │ │ │ ├── levels.md │ │ │ ├── snippets.md │ │ │ ├── tools.md │ │ │ ├── utility.md │ │ │ ├── moderation.md │ │ │ └── index.md │ │ ├── index.md │ │ └── features │ │ │ └── index.md │ ├── assets │ │ ├── stylesheets │ │ │ ├── extra.css │ │ │ └── typography.css │ │ └── images │ │ │ ├── logo.png │ │ │ └── hero-bg.png │ ├── changelog │ │ └── index.md │ ├── reference │ │ ├── src │ │ │ └── tux │ │ │ │ └── index.md │ │ ├── listings.md │ │ ├── coverage.md │ │ ├── cli.md │ │ ├── index.md │ │ └── sbom.md │ ├── tags │ │ └── index.md │ ├── admin │ │ ├── config │ │ │ ├── jail.md │ │ │ ├── commands.md │ │ │ ├── logs.md │ │ │ └── index.md │ │ ├── setup │ │ │ └── index.md │ │ └── index.md │ ├── developer │ │ ├── concepts │ │ │ ├── ui │ │ │ │ ├── embeds.md │ │ │ │ ├── modals.md │ │ │ │ ├── views.md │ │ │ │ ├── buttons.md │ │ │ │ ├── components.md │ │ │ │ └── index.md │ │ │ ├── tasks │ │ │ │ ├── task-monitor.md │ │ │ │ └── index.md │ │ │ ├── handlers │ │ │ │ ├── hot-reload.md │ │ │ │ └── index.md │ │ │ ├── services │ │ │ │ └── index.md │ │ │ ├── index.md │ │ │ ├── shared │ │ │ │ └── index.md │ │ │ ├── wrappers │ │ │ │ └── index.md │ │ │ ├── core │ │ │ │ └── index.md │ │ │ └── database │ │ │ │ └── index.md │ │ ├── best-practices │ │ │ ├── testing │ │ │ │ ├── e2e.md │ │ │ │ ├── unit.md │ │ │ │ ├── integration.md │ │ │ │ └── index.md │ │ │ ├── index.md │ │ │ └── caching.md │ │ ├── tutorials │ │ │ ├── development-setup.md │ │ │ ├── database-integration.md │ │ │ ├── testing-setup.md │ │ │ ├── project-structure.md │ │ │ ├── first-contribution.md │ │ │ ├── index.md │ │ │ ├── creating-first-cog.md │ │ │ └── creating-first-command.md │ │ ├── contributing.md │ │ ├── guides │ │ │ ├── extending-cli.md │ │ │ └── index.md │ │ └── index.md │ ├── getting-started │ │ ├── for-users.md │ │ ├── for-admins.md │ │ ├── for-developers.md │ │ ├── for-self-hosters.md │ │ └── index.md │ ├── faq │ │ └── index.md │ ├── support │ │ ├── troubleshooting │ │ │ ├── index.md │ │ │ ├── user.md │ │ │ ├── selfhost.md │ │ │ ├── admin.md │ │ │ └── developer.md │ │ └── index.md │ └── selfhost │ │ ├── manage │ │ └── index.md │ │ ├── config │ │ └── index.md │ │ ├── index.md │ │ └── install │ │ └── index.md └── overrides │ └── main.html ├── src └── tux │ ├── database │ ├── migrations │ │ ├── versions │ │ │ └── __init__.py │ │ ├── __init__.py │ │ └── script.py.mako │ ├── controllers │ │ └── base │ │ │ ├── __init__.py │ │ │ ├── filters.py │ │ │ └── transaction.py │ ├── __init__.py │ └── models │ │ ├── __init__.py │ │ └── enums.py │ ├── modules │ ├── fun │ │ └── __init__.py │ ├── admin │ │ └── __init__.py │ ├── guild │ │ ├── __init__.py │ │ └── member_count.py │ ├── info │ │ └── __init__.py │ ├── tools │ │ └── __init__.py │ ├── config │ │ ├── __init__.py │ │ ├── overview.py │ │ ├── commands.py │ │ └── logs.py │ ├── features │ │ └── __init__.py │ ├── levels │ │ └── __init__.py │ ├── __init__.py │ ├── moderation │ │ ├── report.py │ │ ├── warn.py │ │ └── kick.py │ └── snippets │ │ └── delete_snippet.py │ ├── services │ ├── wrappers │ │ └── __init__.py │ ├── handlers │ │ ├── error │ │ │ └── __init__.py │ │ └── __init__.py │ ├── hot_reload │ │ ├── __init__.py │ │ └── cog.py │ ├── __init__.py │ ├── moderation │ │ ├── __init__.py │ │ └── factory.py │ └── sentry │ │ └── cog.py │ ├── help │ └── __init__.py │ ├── plugins │ ├── atl │ │ └── __init__.py │ ├── __init__.py │ └── README.md │ ├── core │ ├── setup │ │ ├── __init__.py │ │ ├── permission_setup.py │ │ └── cog_setup.py │ ├── types.py │ ├── __init__.py │ └── checks.py │ ├── ui │ ├── modals │ │ └── __init__.py │ ├── views │ │ ├── config │ │ │ └── __init__.py │ │ └── __init__.py │ ├── __init__.py │ └── buttons.py │ ├── shared │ ├── __init__.py │ └── config │ │ ├── __init__.py │ │ └── generators │ │ ├── __init__.py │ │ └── base.py │ ├── __init__.py │ └── main.py ├── tests ├── __init__.py ├── fixtures │ ├── pglite_fixtures.py │ ├── __init__.py │ └── test_data_fixtures.py ├── unit │ ├── __init__.py │ └── test_migration_url_conversion.py ├── integration │ └── __init__.py └── e2e │ └── __init__.py ├── assets ├── emojis │ ├── ban.png │ ├── jail.png │ ├── kick.png │ ├── warn.png │ ├── added.png │ ├── removed.png │ ├── tempban.png │ ├── timeout.png │ ├── tux_case.png │ ├── tux_error.png │ ├── tux_info.png │ ├── tux_note.png │ ├── tux_poll.png │ ├── tux_tag.png │ ├── active_case.png │ ├── snippetban.png │ ├── tux_default.png │ ├── tux_notify.png │ ├── tux_prefix.png │ ├── tux_success.png │ ├── inactive_case.png │ └── snippetunban.png ├── badges │ ├── lucky.avif │ ├── helpful.avif │ ├── tux-dev.avif │ ├── 500h-voice.avif │ ├── top50-text.avif │ ├── 100k-messages.avif │ ├── 10k-messages.avif │ ├── 50k-messages.avif │ ├── day1-joiner.avif │ └── former-staff.avif ├── branding │ ├── tux.gif │ └── avatar.avif ├── readme-banner.png ├── roles │ ├── de-wm │ │ ├── dwm.avif │ │ ├── i3.avif │ │ ├── jwm.avif │ │ ├── bspwm.avif │ │ ├── exwm.avif │ │ ├── gnome.avif │ │ ├── lx_qt.avif │ │ ├── mate.avif │ │ ├── qtile.avif │ │ ├── river.avif │ │ ├── xfce.avif │ │ ├── Cinnamon.avif │ │ ├── awesome.avif │ │ ├── berrywm.avif │ │ ├── budgie.avif │ │ ├── cosmic.avif │ │ ├── hyprland.avif │ │ ├── ice_wm.avif │ │ ├── left_wm.avif │ │ ├── openbox.avif │ │ ├── stump_wm.avif │ │ ├── sway_wm.avif │ │ ├── wayfire.avif │ │ ├── xmonad.avif │ │ ├── herbsluft.avif │ │ ├── kde_plasma.avif │ │ └── enlightenment.avif │ ├── distro │ │ ├── mx.avif │ │ ├── arch.avif │ │ ├── arco.avif │ │ ├── kiss.avif │ │ ├── lfs.avif │ │ ├── mint.avif │ │ ├── void.avif │ │ ├── alpine.avif │ │ ├── anti_x.avif │ │ ├── antix.avif │ │ ├── artix.avif │ │ ├── bazzite.avif │ │ ├── bedrock.avif │ │ ├── cachy.avif │ │ ├── chimera.avif │ │ ├── debian.avif │ │ ├── deepin.avif │ │ ├── devuan.avif │ │ ├── exherbo.avif │ │ ├── fedora.avif │ │ ├── garuda.avif │ │ ├── gentoo.avif │ │ ├── haiku.avif │ │ ├── mac_os.avif │ │ ├── manjaro.avif │ │ ├── net_bsd.avif │ │ ├── nixos.avif │ │ ├── nobara.avif │ │ ├── plan_9.avif │ │ ├── popos.avif │ │ ├── puppy.avif │ │ ├── qubes.avif │ │ ├── redhat.avif │ │ ├── solus.avif │ │ ├── ubuntu.avif │ │ ├── vanilla.avif │ │ ├── windows.avif │ │ ├── zorin.avif │ │ ├── endeavour.avif │ │ ├── free_bsd.avif │ │ ├── open_bsd.avif │ │ ├── opensuse.avif │ │ ├── slackware.avif │ │ ├── asahi_linux.avif │ │ ├── rocky_linux.avif │ │ └── ubuntu_mate.avif │ ├── langs │ │ ├── asm.avif │ │ ├── c.avif │ │ ├── cpp.avif │ │ ├── go.avif │ │ ├── js.avif │ │ ├── lua.avif │ │ ├── nim.avif │ │ ├── php.avif │ │ ├── r.avif │ │ ├── zig.avif │ │ ├── bash.avif │ │ ├── dart.avif │ │ ├── elixr.avif │ │ ├── java.avif │ │ ├── julia.avif │ │ ├── lisp.avif │ │ ├── perl.avif │ │ ├── ruby.avif │ │ ├── rust.avif │ │ ├── swift.avif │ │ ├── vala.avif │ │ ├── c_sharp.avif │ │ ├── clojure.avif │ │ ├── crystal.avif │ │ ├── erlang.avif │ │ ├── haskell.avif │ │ ├── html_css.avif │ │ ├── kotlin.avif │ │ ├── o_caml.avif │ │ ├── python.avif │ │ ├── gd_script.avif │ │ └── sh_script.avif │ ├── text-editors │ │ ├── ed.avif │ │ ├── emacs.avif │ │ ├── helix.avif │ │ ├── kate.avif │ │ ├── micro.avif │ │ ├── nano.avif │ │ ├── neovim.avif │ │ ├── kakoune.avif │ │ ├── vs_code.avif │ │ └── jetbrains.avif │ └── donor-icons │ │ ├── donor.avif │ │ ├── mega-donor.avif │ │ └── super-donor.avif └── embeds │ ├── active_case.avif │ └── inactive_case.avif ├── typings ├── typer │ ├── __main__.pyi │ ├── _types.pyi │ ├── colors.pyi │ ├── testing.pyi │ ├── completion.pyi │ ├── __init__.pyi │ ├── _completion_shared.pyi │ ├── _typing.pyi │ ├── utils.pyi │ ├── _completion_classes.pyi │ └── cli.pyi ├── emojis │ ├── db │ │ ├── db.pyi │ │ ├── __init__.pyi │ │ └── utils.pyi │ ├── __init__.pyi │ └── emojis.pyi ├── pydantic_settings_export │ ├── generators │ │ ├── __init__.pyi │ │ ├── simple.pyi │ │ └── dotenv.pyi │ ├── constants.pyi │ ├── settings.pyi │ ├── sources.pyi │ └── utils.pyi ├── py_pglite │ ├── extensions.pyi │ ├── __init__.pyi │ ├── sqlalchemy │ │ ├── __init__.pyi │ │ ├── fixtures.pyi │ │ └── manager.pyi │ └── config.pyi └── reactionmenu │ ├── __init__.pyi │ └── decorators.pyi ├── .cursor └── rules │ └── rules.mdc ├── .git-blame-ignore-revs ├── .trivyignore ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ ├── question.md │ └── bug_report.md ├── SECURITY.md ├── scripts │ ├── docs.sh │ ├── shared.sh │ └── docker.sh ├── actions │ ├── create-test-env │ │ └── action.yml │ └── action-basedpyright │ │ └── action.yml └── PULL_REQUEST_TEMPLATE.md ├── shell.nix ├── docker └── adminer │ └── index.php ├── scripts └── __init__.py ├── .markdownlintignore ├── .dockerignore ├── .vscode ├── extensions.json └── python.code-snippets ├── .yamllint.yml ├── .docstr.yaml ├── .devcontainer └── post-create.sh ├── compose.override.yaml.example ├── flake.nix ├── alembic.ini ├── config └── config.json.example ├── flake.lock └── .editorconfig /.python-version: -------------------------------------------------------------------------------- 1 | 3.13.8 2 | -------------------------------------------------------------------------------- /docs/content/user/modules/fun.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/user/modules/info.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/user/modules/levels.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/user/modules/snippets.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/user/modules/tools.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/user/modules/utility.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/user/modules/moderation.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/tux/database/migrations/versions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Testing suite for Tux.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/fun/__init__.py: -------------------------------------------------------------------------------- 1 | """Fun cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/admin/__init__.py: -------------------------------------------------------------------------------- 1 | """Admin cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/guild/__init__.py: -------------------------------------------------------------------------------- 1 | """Guild cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/info/__init__.py: -------------------------------------------------------------------------------- 1 | """Info cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/tools/__init__.py: -------------------------------------------------------------------------------- 1 | """Tools cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/config/__init__.py: -------------------------------------------------------------------------------- 1 | """Config cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/features/__init__.py: -------------------------------------------------------------------------------- 1 | """Services cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/modules/levels/__init__.py: -------------------------------------------------------------------------------- 1 | """Levels cog group for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /src/tux/database/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | """Database migrations for Tux Bot.""" 2 | -------------------------------------------------------------------------------- /assets/emojis/ban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/ban.png -------------------------------------------------------------------------------- /assets/emojis/jail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/jail.png -------------------------------------------------------------------------------- /assets/emojis/kick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/kick.png -------------------------------------------------------------------------------- /assets/emojis/warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/warn.png -------------------------------------------------------------------------------- /docs/content/assets/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | .md-grid { 2 | max-width: 68rem !important; 3 | } 4 | -------------------------------------------------------------------------------- /typings/typer/__main__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | -------------------------------------------------------------------------------- /assets/badges/lucky.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/lucky.avif -------------------------------------------------------------------------------- /assets/branding/tux.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/branding/tux.gif -------------------------------------------------------------------------------- /assets/emojis/added.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/added.png -------------------------------------------------------------------------------- /assets/readme-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/readme-banner.png -------------------------------------------------------------------------------- /assets/badges/helpful.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/helpful.avif -------------------------------------------------------------------------------- /assets/badges/tux-dev.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/tux-dev.avif -------------------------------------------------------------------------------- /assets/branding/avatar.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/branding/avatar.avif -------------------------------------------------------------------------------- /assets/emojis/removed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/removed.png -------------------------------------------------------------------------------- /assets/emojis/tempban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tempban.png -------------------------------------------------------------------------------- /assets/emojis/timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/timeout.png -------------------------------------------------------------------------------- /assets/emojis/tux_case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_case.png -------------------------------------------------------------------------------- /assets/emojis/tux_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_error.png -------------------------------------------------------------------------------- /assets/emojis/tux_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_info.png -------------------------------------------------------------------------------- /assets/emojis/tux_note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_note.png -------------------------------------------------------------------------------- /assets/emojis/tux_poll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_poll.png -------------------------------------------------------------------------------- /assets/emojis/tux_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_tag.png -------------------------------------------------------------------------------- /assets/roles/de-wm/dwm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/dwm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/i3.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/i3.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/jwm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/jwm.avif -------------------------------------------------------------------------------- /assets/roles/distro/mx.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/mx.avif -------------------------------------------------------------------------------- /assets/roles/langs/asm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/asm.avif -------------------------------------------------------------------------------- /assets/roles/langs/c.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/c.avif -------------------------------------------------------------------------------- /assets/roles/langs/cpp.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/cpp.avif -------------------------------------------------------------------------------- /assets/roles/langs/go.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/go.avif -------------------------------------------------------------------------------- /assets/roles/langs/js.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/js.avif -------------------------------------------------------------------------------- /assets/roles/langs/lua.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/lua.avif -------------------------------------------------------------------------------- /assets/roles/langs/nim.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/nim.avif -------------------------------------------------------------------------------- /assets/roles/langs/php.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/php.avif -------------------------------------------------------------------------------- /assets/roles/langs/r.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/r.avif -------------------------------------------------------------------------------- /assets/roles/langs/zig.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/zig.avif -------------------------------------------------------------------------------- /src/tux/services/wrappers/__init__.py: -------------------------------------------------------------------------------- 1 | """Wrappers for external services such as Github and Godbolt.""" 2 | -------------------------------------------------------------------------------- /assets/badges/500h-voice.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/500h-voice.avif -------------------------------------------------------------------------------- /assets/badges/top50-text.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/top50-text.avif -------------------------------------------------------------------------------- /assets/emojis/active_case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/active_case.png -------------------------------------------------------------------------------- /assets/emojis/snippetban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/snippetban.png -------------------------------------------------------------------------------- /assets/emojis/tux_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_default.png -------------------------------------------------------------------------------- /assets/emojis/tux_notify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_notify.png -------------------------------------------------------------------------------- /assets/emojis/tux_prefix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_prefix.png -------------------------------------------------------------------------------- /assets/emojis/tux_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/tux_success.png -------------------------------------------------------------------------------- /assets/roles/de-wm/bspwm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/bspwm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/exwm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/exwm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/gnome.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/gnome.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/lx_qt.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/lx_qt.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/mate.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/mate.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/qtile.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/qtile.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/river.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/river.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/xfce.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/xfce.avif -------------------------------------------------------------------------------- /assets/roles/distro/arch.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/arch.avif -------------------------------------------------------------------------------- /assets/roles/distro/arco.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/arco.avif -------------------------------------------------------------------------------- /assets/roles/distro/kiss.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/kiss.avif -------------------------------------------------------------------------------- /assets/roles/distro/lfs.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/lfs.avif -------------------------------------------------------------------------------- /assets/roles/distro/mint.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/mint.avif -------------------------------------------------------------------------------- /assets/roles/distro/void.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/void.avif -------------------------------------------------------------------------------- /assets/roles/langs/bash.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/bash.avif -------------------------------------------------------------------------------- /assets/roles/langs/dart.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/dart.avif -------------------------------------------------------------------------------- /assets/roles/langs/elixr.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/elixr.avif -------------------------------------------------------------------------------- /assets/roles/langs/java.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/java.avif -------------------------------------------------------------------------------- /assets/roles/langs/julia.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/julia.avif -------------------------------------------------------------------------------- /assets/roles/langs/lisp.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/lisp.avif -------------------------------------------------------------------------------- /assets/roles/langs/perl.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/perl.avif -------------------------------------------------------------------------------- /assets/roles/langs/ruby.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/ruby.avif -------------------------------------------------------------------------------- /assets/roles/langs/rust.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/rust.avif -------------------------------------------------------------------------------- /assets/roles/langs/swift.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/swift.avif -------------------------------------------------------------------------------- /assets/roles/langs/vala.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/vala.avif -------------------------------------------------------------------------------- /assets/badges/100k-messages.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/100k-messages.avif -------------------------------------------------------------------------------- /assets/badges/10k-messages.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/10k-messages.avif -------------------------------------------------------------------------------- /assets/badges/50k-messages.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/50k-messages.avif -------------------------------------------------------------------------------- /assets/badges/day1-joiner.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/day1-joiner.avif -------------------------------------------------------------------------------- /assets/badges/former-staff.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/badges/former-staff.avif -------------------------------------------------------------------------------- /assets/embeds/active_case.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/embeds/active_case.avif -------------------------------------------------------------------------------- /assets/embeds/inactive_case.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/embeds/inactive_case.avif -------------------------------------------------------------------------------- /assets/emojis/inactive_case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/inactive_case.png -------------------------------------------------------------------------------- /assets/emojis/snippetunban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/emojis/snippetunban.png -------------------------------------------------------------------------------- /assets/roles/de-wm/Cinnamon.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/Cinnamon.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/awesome.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/awesome.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/berrywm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/berrywm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/budgie.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/budgie.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/cosmic.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/cosmic.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/hyprland.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/hyprland.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/ice_wm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/ice_wm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/left_wm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/left_wm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/openbox.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/openbox.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/stump_wm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/stump_wm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/sway_wm.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/sway_wm.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/wayfire.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/wayfire.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/xmonad.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/xmonad.avif -------------------------------------------------------------------------------- /assets/roles/distro/alpine.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/alpine.avif -------------------------------------------------------------------------------- /assets/roles/distro/anti_x.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/anti_x.avif -------------------------------------------------------------------------------- /assets/roles/distro/antix.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/antix.avif -------------------------------------------------------------------------------- /assets/roles/distro/artix.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/artix.avif -------------------------------------------------------------------------------- /assets/roles/distro/bazzite.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/bazzite.avif -------------------------------------------------------------------------------- /assets/roles/distro/bedrock.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/bedrock.avif -------------------------------------------------------------------------------- /assets/roles/distro/cachy.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/cachy.avif -------------------------------------------------------------------------------- /assets/roles/distro/chimera.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/chimera.avif -------------------------------------------------------------------------------- /assets/roles/distro/debian.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/debian.avif -------------------------------------------------------------------------------- /assets/roles/distro/deepin.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/deepin.avif -------------------------------------------------------------------------------- /assets/roles/distro/devuan.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/devuan.avif -------------------------------------------------------------------------------- /assets/roles/distro/exherbo.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/exherbo.avif -------------------------------------------------------------------------------- /assets/roles/distro/fedora.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/fedora.avif -------------------------------------------------------------------------------- /assets/roles/distro/garuda.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/garuda.avif -------------------------------------------------------------------------------- /assets/roles/distro/gentoo.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/gentoo.avif -------------------------------------------------------------------------------- /assets/roles/distro/haiku.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/haiku.avif -------------------------------------------------------------------------------- /assets/roles/distro/mac_os.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/mac_os.avif -------------------------------------------------------------------------------- /assets/roles/distro/manjaro.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/manjaro.avif -------------------------------------------------------------------------------- /assets/roles/distro/net_bsd.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/net_bsd.avif -------------------------------------------------------------------------------- /assets/roles/distro/nixos.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/nixos.avif -------------------------------------------------------------------------------- /assets/roles/distro/nobara.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/nobara.avif -------------------------------------------------------------------------------- /assets/roles/distro/plan_9.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/plan_9.avif -------------------------------------------------------------------------------- /assets/roles/distro/popos.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/popos.avif -------------------------------------------------------------------------------- /assets/roles/distro/puppy.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/puppy.avif -------------------------------------------------------------------------------- /assets/roles/distro/qubes.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/qubes.avif -------------------------------------------------------------------------------- /assets/roles/distro/redhat.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/redhat.avif -------------------------------------------------------------------------------- /assets/roles/distro/solus.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/solus.avif -------------------------------------------------------------------------------- /assets/roles/distro/ubuntu.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/ubuntu.avif -------------------------------------------------------------------------------- /assets/roles/distro/vanilla.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/vanilla.avif -------------------------------------------------------------------------------- /assets/roles/distro/windows.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/windows.avif -------------------------------------------------------------------------------- /assets/roles/distro/zorin.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/zorin.avif -------------------------------------------------------------------------------- /assets/roles/langs/c_sharp.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/c_sharp.avif -------------------------------------------------------------------------------- /assets/roles/langs/clojure.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/clojure.avif -------------------------------------------------------------------------------- /assets/roles/langs/crystal.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/crystal.avif -------------------------------------------------------------------------------- /assets/roles/langs/erlang.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/erlang.avif -------------------------------------------------------------------------------- /assets/roles/langs/haskell.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/haskell.avif -------------------------------------------------------------------------------- /assets/roles/langs/html_css.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/html_css.avif -------------------------------------------------------------------------------- /assets/roles/langs/kotlin.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/kotlin.avif -------------------------------------------------------------------------------- /assets/roles/langs/o_caml.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/o_caml.avif -------------------------------------------------------------------------------- /assets/roles/langs/python.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/python.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/herbsluft.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/herbsluft.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/kde_plasma.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/kde_plasma.avif -------------------------------------------------------------------------------- /assets/roles/distro/endeavour.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/endeavour.avif -------------------------------------------------------------------------------- /assets/roles/distro/free_bsd.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/free_bsd.avif -------------------------------------------------------------------------------- /assets/roles/distro/open_bsd.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/open_bsd.avif -------------------------------------------------------------------------------- /assets/roles/distro/opensuse.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/opensuse.avif -------------------------------------------------------------------------------- /assets/roles/distro/slackware.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/slackware.avif -------------------------------------------------------------------------------- /assets/roles/langs/gd_script.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/gd_script.avif -------------------------------------------------------------------------------- /assets/roles/langs/sh_script.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/langs/sh_script.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/ed.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/ed.avif -------------------------------------------------------------------------------- /assets/roles/de-wm/enlightenment.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/de-wm/enlightenment.avif -------------------------------------------------------------------------------- /assets/roles/distro/asahi_linux.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/asahi_linux.avif -------------------------------------------------------------------------------- /assets/roles/distro/rocky_linux.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/rocky_linux.avif -------------------------------------------------------------------------------- /assets/roles/distro/ubuntu_mate.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/distro/ubuntu_mate.avif -------------------------------------------------------------------------------- /assets/roles/donor-icons/donor.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/donor-icons/donor.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/emacs.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/emacs.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/helix.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/helix.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/kate.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/kate.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/micro.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/micro.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/nano.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/nano.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/neovim.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/neovim.avif -------------------------------------------------------------------------------- /docs/content/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/docs/content/assets/images/logo.png -------------------------------------------------------------------------------- /assets/roles/text-editors/kakoune.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/kakoune.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/vs_code.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/vs_code.avif -------------------------------------------------------------------------------- /docs/content/assets/images/hero-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/docs/content/assets/images/hero-bg.png -------------------------------------------------------------------------------- /typings/emojis/db/db.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | Emoji = ... 6 | EMOJI_DB = ... 7 | -------------------------------------------------------------------------------- /assets/roles/donor-icons/mega-donor.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/donor-icons/mega-donor.avif -------------------------------------------------------------------------------- /assets/roles/donor-icons/super-donor.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/donor-icons/super-donor.avif -------------------------------------------------------------------------------- /assets/roles/text-editors/jetbrains.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/allthingslinux/tux/HEAD/assets/roles/text-editors/jetbrains.avif -------------------------------------------------------------------------------- /docs/content/changelog/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | icon: material/script-text-outline 4 | --- 5 | 6 | --8<-- "CHANGELOG.md" 7 | -------------------------------------------------------------------------------- /.cursor/rules/rules.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | alwaysApply: true 3 | --- 4 | # Rules 5 | 6 | See [AGENTS.md](../../AGENTS.md) for the coding agent rules. 7 | -------------------------------------------------------------------------------- /docs/content/reference/src/tux/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | icon: material/code-tags 4 | tags: 5 | - reference 6 | - api 7 | --- 8 | -------------------------------------------------------------------------------- /src/tux/services/handlers/error/__init__.py: -------------------------------------------------------------------------------- 1 | """Error handling system for Tux Discord bot.""" 2 | 3 | from .cog import ErrorHandler 4 | 5 | __all__ = ["ErrorHandler"] 6 | -------------------------------------------------------------------------------- /docs/content/reference/listings.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Listings 3 | hide: 4 | - toc 5 | tags: 6 | - reference 7 | - code-snippets 8 | --- 9 | --- 10 | 11 | # Listings 12 | -------------------------------------------------------------------------------- /src/tux/services/hot_reload/__init__.py: -------------------------------------------------------------------------------- 1 | """Hot reload system for Tux Discord bot.""" 2 | 3 | from .cog import setup 4 | from .service import HotReload 5 | 6 | __all__ = ["HotReload", "setup"] 7 | -------------------------------------------------------------------------------- /src/tux/database/controllers/base/__init__.py: -------------------------------------------------------------------------------- 1 | """Database controller components for modular database operations.""" 2 | 3 | from .base_controller import BaseController 4 | 5 | __all__ = ["BaseController"] 6 | -------------------------------------------------------------------------------- /docs/content/reference/coverage.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Coverage 3 | hide: 4 | - toc 5 | tags: 6 | - testing 7 | - ci-cd 8 | - codecov 9 | - pytest 10 | - coverage 11 | --- 12 | 13 | # Coverage 14 | -------------------------------------------------------------------------------- /src/tux/help/__init__.py: -------------------------------------------------------------------------------- 1 | """Refactored help system with separated concerns.""" 2 | 3 | # Import only what's needed externally to avoid circular imports 4 | from .help import TuxHelp 5 | 6 | __all__ = ["TuxHelp"] 7 | -------------------------------------------------------------------------------- /tests/fixtures/pglite_fixtures.py: -------------------------------------------------------------------------------- 1 | """PGlite process management fixtures - cleanup functionality removed.""" 2 | 3 | # PGlite cleanup functionality has been removed as it's no longer needed 4 | # due to upstream fixes in the py-pglite library. 5 | -------------------------------------------------------------------------------- /src/tux/plugins/atl/__init__.py: -------------------------------------------------------------------------------- 1 | """Custom plugins package for user-defined extensions. 2 | 3 | This package is intended for custom modules created by self-hosters. 4 | Modules placed here will be automatically discovered and loaded by the bot. 5 | """ 6 | -------------------------------------------------------------------------------- /typings/emojis/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from .emojis import count, decode, encode, get, iter 6 | 7 | ''' 8 | Emojis for Python 🐍 9 | ''' 10 | __all__ = ['encode', 'decode', 'get', 'count', 'iter'] 11 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Ignore bulk formatting changes in git blame 2 | # Usage: git config blame.ignoreRevsFile .git-blame-ignore-revs 3 | 4 | # Line length change from 120 to 88 characters 5 | a74591ab342bd3e49cb80cada4a9656b1bf5ca1e # Line length formatting change 6 | -------------------------------------------------------------------------------- /src/tux/core/setup/__init__.py: -------------------------------------------------------------------------------- 1 | """Setup services for bot initialization.""" 2 | 3 | from .base import BaseSetupService, BotSetupService 4 | from .orchestrator import BotSetupOrchestrator 5 | 6 | __all__ = ["BaseSetupService", "BotSetupOrchestrator", "BotSetupService"] 7 | -------------------------------------------------------------------------------- /src/tux/ui/modals/__init__.py: -------------------------------------------------------------------------------- 1 | """Modal components for Discord UI interactions. 2 | 3 | This module contains modal dialog components for user input. 4 | """ 5 | 6 | from tux.ui.modals.report import ReportModal 7 | 8 | __all__ = [ 9 | "ReportModal", 10 | ] 11 | -------------------------------------------------------------------------------- /docs/content/tags/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tags 3 | icon: material/tag-outline 4 | --- 5 | 6 | # Tags 7 | 8 | !!! warning "Work in progress" 9 | This section is a work in progress. Please help us by contributing to the documentation. 10 | 11 | 12 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/generators/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from .abstract import AbstractGenerator 6 | from .dotenv import * 7 | from .markdown import * 8 | from .simple import * 9 | 10 | Generators = ... 11 | -------------------------------------------------------------------------------- /src/tux/shared/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Shared utilities and components for Tux. 3 | 4 | This module contains code that can be shared across all applications 5 | (bot, CLI, future web/API applications) including constants, exceptions, 6 | configuration management, and generic helper functions. 7 | """ 8 | -------------------------------------------------------------------------------- /src/tux/shared/config/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration management for Tux. 3 | 4 | This package provides configuration loading. 5 | No environment concepts - just use DEBUG for conditional logic. 6 | """ 7 | 8 | from .settings import CONFIG 9 | 10 | __all__ = [ 11 | "CONFIG", 12 | ] 13 | -------------------------------------------------------------------------------- /docs/content/admin/config/jail.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Jail Configuration 3 | tags: 4 | - admin-guide 5 | - configuration 6 | - jail 7 | --- 8 | 9 | # Jail 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/reference/cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CLI Reference 3 | tags: 4 | - cli 5 | - typer 6 | - commands 7 | - terminal 8 | --- 9 | 10 | # CLI Reference 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/admin/config/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Commands 3 | tags: 4 | - admin-guide 5 | - configuration 6 | - commands 7 | --- 8 | 9 | # Commands Configuration 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/admin/config/logs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Logs Configuration 3 | tags: 4 | - admin-guide 5 | - configuration 6 | - logs 7 | --- 8 | 9 | # Logs Configuration 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/ui/embeds.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Embeds 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - ui 7 | - embeds 8 | --- 9 | 10 | # Embeds 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/ui/modals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Modals 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - ui 7 | - components 8 | --- 9 | 10 | # Modals 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/ui/views.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Views 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - ui 7 | - components 8 | --- 9 | 10 | # Views 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/getting-started/for-users.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started for Users 3 | tags: 4 | - getting-started 5 | - user 6 | --- 7 | 8 | # Getting Started for Users 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | -------------------------------------------------------------------------------- /src/tux/services/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Services layer for Tux bot. 3 | 4 | This module contains backend services including database access, 5 | external API wrappers, event handlers, and infrastructure services. 6 | """ 7 | 8 | from tux.services.http_client import http_client 9 | 10 | __all__ = ["http_client"] 11 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/tasks/task-monitor.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Task Monitor 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - tasks 7 | --- 8 | 9 | # Task Monitor 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/ui/buttons.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Buttons 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - ui 7 | - components 8 | --- 9 | 10 | # Buttons 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/getting-started/for-admins.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started for Admins 3 | tags: 4 | - getting-started 5 | - admin 6 | --- 7 | 8 | # Getting Started for Admins 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | -------------------------------------------------------------------------------- /docs/content/reference/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reference 3 | --- 4 | 5 | # Reference 6 | 7 | !!! warning "Work in progress" 8 | This section is a work in progress. Please help us by contributing to the documentation. 9 | 10 | This section contains reference material for Tux. 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/content/developer/best-practices/testing/e2e.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: E2E Testing 3 | tags: 4 | - developer-guide 5 | - best-practices 6 | - testing 7 | --- 8 | 9 | # E2E Testing 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/developer/best-practices/testing/unit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Unit Testing 3 | tags: 4 | - developer-guide 5 | - best-practices 6 | - testing 7 | --- 8 | 9 | # Unit Testing 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/ui/components.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Components 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - ui 7 | - components 8 | --- 9 | 10 | # Components 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/development-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Development Setup 3 | tags: 4 | - developer-guide 5 | - tutorials 6 | - setup 7 | --- 8 | 9 | # Development Setup 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/getting-started/for-developers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started for Developers 3 | tags: 4 | - getting-started 5 | - developer 6 | --- 7 | 8 | # Getting Started for Developers 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unit tests for Tux database components. 3 | 4 | These tests focus on individual components in isolation: 5 | - Model validation and relationships 6 | - PostgreSQL features and model behavior 7 | - Fast, isolated testing with py-pglite 8 | 9 | Run with: pytest tests/unit/ or pytest -m unit 10 | """ 11 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/handlers/hot-reload.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hot Reload 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - handlers 7 | - development 8 | --- 9 | 10 | # Hot Reload 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/getting-started/for-self-hosters.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started for Self-Hosters 3 | tags: 4 | - getting-started 5 | - selfhost 6 | --- 7 | 8 | # Getting Started for Self-Hosters 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | -------------------------------------------------------------------------------- /typings/py_pglite/extensions.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | """Extension management for py-pglite. 6 | 7 | This module provides a registry of supported PGlite extensions and the 8 | necessary JavaScript import details for each. 9 | """ 10 | SUPPORTED_EXTENSIONS: dict[str, dict[str, str]] = ... 11 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/database-integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Database Integration 3 | tags: 4 | - developer-guide 5 | - tutorials 6 | - database 7 | --- 8 | 9 | # Database Integration 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/developer/best-practices/testing/integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Integration Testing 3 | tags: 4 | - developer-guide 5 | - best-practices 6 | - testing 7 | --- 8 | 9 | # Integration Testing 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | -------------------------------------------------------------------------------- /docs/content/faq/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FAQ 3 | icon: material/help-circle-outline 4 | --- 5 | 6 | # Frequently Asked Questions 7 | 8 | !!! warning "Work in progress" 9 | This section is a work in progress. Please help us by contributing to the documentation. 10 | 11 | This page contains frequently asked questions and answers about Tux. 12 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/constants.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from pathlib import Path 6 | from typing import Annotated 7 | from pydantic import BeforeValidator 8 | 9 | __all__ = ("StrAsPath", "FIELD_TYPE_MAP") 10 | StrAsPath = Annotated[Path, BeforeValidator(lambda v: Path(v))] 11 | FIELD_TYPE_MAP = ... 12 | -------------------------------------------------------------------------------- /docs/content/developer/contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing 3 | tags: 4 | - developer-guide 5 | - contributing 6 | --- 7 | 8 | # Contributing 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains information on how to contribute to Tux. 14 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Integration tests for Tux database components. 3 | 4 | These tests verify component interactions and system behavior: 5 | - Database setup scenarios 6 | - Complete database workflows 7 | - Self-hosting simulation 8 | - Error handling and edge cases 9 | 10 | Run with: pytest tests/integration/ or pytest -m integration 11 | """ 12 | -------------------------------------------------------------------------------- /src/tux/core/types.py: -------------------------------------------------------------------------------- 1 | """Type definitions for Tux core components.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TypeVar 6 | 7 | import discord 8 | from discord.ext import commands 9 | 10 | # Type variable for generic context types 11 | T = TypeVar("T", bound=commands.Context[commands.Bot] | discord.Interaction) 12 | 13 | __all__ = ["T"] 14 | -------------------------------------------------------------------------------- /docs/content/developer/guides/extending-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Extending CLI 3 | description: How to add new CLI commands and tools using Typer. 4 | tags: 5 | - developer-guide 6 | - guides 7 | - cli 8 | --- 9 | 10 | # Extending CLI 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/testing-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing Setup 3 | tags: 4 | - developer-guide 5 | - tutorials 6 | - testing 7 | --- 8 | 9 | # Testing Setup 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains the testing setup for Tux. 15 | -------------------------------------------------------------------------------- /docs/content/support/troubleshooting/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Troubleshooting 3 | tags: 4 | - support 5 | - troubleshooting 6 | --- 7 | 8 | # Troubleshooting 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | Common issues and their solutions. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/support/troubleshooting/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: User Troubleshooting 3 | tags: 4 | - support 5 | - troubleshooting 6 | - user 7 | --- 8 | 9 | # User Troubleshooting 10 | 11 | Common issues users encounter and their solutions. 12 | 13 | !!! warning "Work in progress" 14 | This section is a work in progress. Please help us by contributing to the documentation. 15 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/tasks/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tasks 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - tasks 7 | --- 8 | 9 | # Tasks 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts related to tasks. 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/tux/database/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tux Database Module. 3 | 4 | This module provides the core database functionality for the Tux Discord bot, 5 | including SQLModel-based models, controllers for database operations, and 6 | a unified database service interface. 7 | """ 8 | 9 | from .service import DatabaseService 10 | 11 | # Clean, unified database service 12 | __all__ = ["DatabaseService"] 13 | -------------------------------------------------------------------------------- /docs/content/admin/setup/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Admin Setup 3 | tags: 4 | - admin-guide 5 | - setup 6 | --- 7 | 8 | # Admin Setup 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains documentation for setting up Tux on your Discord server. 14 | 15 | ## Invite Tux to Your Server 16 | -------------------------------------------------------------------------------- /docs/content/developer/guides/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developer Guides 3 | tags: 4 | - developer-guide 5 | - guides 6 | --- 7 | 8 | # Developer Guides 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains guides for developers working with Tux. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/project-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Project Structure 3 | tags: 4 | - developer-guide 5 | - tutorials 6 | - architecture 7 | --- 8 | 9 | # Project Structure 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains the project structure for Tux. 15 | -------------------------------------------------------------------------------- /tests/e2e/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | End-to-end tests for Tux database workflows. 3 | 4 | These tests simulate complete user journeys and real-world scenarios: 5 | - First-time bot setup workflows 6 | - Complete feature usage scenarios 7 | - Data migration between versions 8 | - Scalability and performance testing 9 | - Disaster recovery scenarios 10 | 11 | Run with: pytest --run-e2e tests/e2e/ 12 | """ 13 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/handlers/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Handlers 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - handlers 7 | --- 8 | 9 | # Handlers 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts related to handlers. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/services/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Services 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - services 7 | --- 8 | 9 | # Services 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts related to services. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/ui/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: UI Concepts 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - ui 7 | --- 8 | 9 | # UI Concepts 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts related to UI components. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/support/troubleshooting/selfhost.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Self-Host Troubleshooting 3 | tags: 4 | - support 5 | - troubleshooting 6 | - selfhost 7 | --- 8 | 9 | # Self-Host Troubleshooting 10 | 11 | Common issues when self-hosting Tux and their solutions. 12 | 13 | !!! warning "Work in progress" 14 | This section is a work in progress. Please help us by contributing to the documentation. 15 | -------------------------------------------------------------------------------- /typings/typer/_types.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import click 6 | from typing import Generic, TypeVar, Union 7 | 8 | ParamTypeValue = TypeVar("ParamTypeValue") 9 | class TyperChoice(click.Choice, Generic[ParamTypeValue]): 10 | def normalize_choice(self, choice: ParamTypeValue, ctx: Union[click.Context, None]) -> str: 11 | ... 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developer Concepts 3 | tags: 4 | - developer-guide 5 | - concepts 6 | --- 7 | 8 | # Developer Concepts 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains concepts that are used in the codebase. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/first-contribution.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: First Contribution 3 | description: How to make your first contribution to the Tux codebase 4 | tags: 5 | - developer-guide 6 | - tutorials 7 | - contributing 8 | --- 9 | 10 | # First Contribution 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /docs/content/selfhost/manage/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Self-Host Management 3 | tags: 4 | - selfhost 5 | - operations 6 | --- 7 | 8 | # Self-Host Management 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains documentation for managing your Tux instance. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/support/troubleshooting/admin.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Admin Troubleshooting 3 | tags: 4 | - support 5 | - troubleshooting 6 | - admin 7 | --- 8 | 9 | # Admin Troubleshooting 10 | 11 | Common issues administrators encounter when using Tux and their solutions. 12 | 13 | !!! warning "Work in progress" 14 | This section is a work in progress. Please help us by contributing to the documentation. 15 | -------------------------------------------------------------------------------- /src/tux/services/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | """Event handlers for Tux Bot for activities, Discord events, error handling and event handling. 2 | 3 | This is a namespace package - individual handlers are imported directly 4 | from their submodules (e.g., from tux.services.handlers.error import formatter) 5 | rather than through this package. 6 | """ 7 | 8 | # Namespace package - no direct exports 9 | __all__ = [] 10 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/shared/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Shared 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - shared 7 | --- 8 | 9 | # Shared 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts that are shared across the codebase. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/wrappers/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Wrappers 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - wrappers 7 | --- 8 | 9 | # Wrappers 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts related to service wrappers. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developer Tutorials 3 | tags: 4 | - developer-guide 5 | - tutorials 6 | --- 7 | 8 | # Developer Tutorials 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains tutorials for developers working with Tux. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/user/modules/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: User Commands 3 | tags: 4 | - user-guide 5 | - modules 6 | - commands 7 | --- 8 | 9 | # User Commands 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains documentation for the commands available in Tux. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/admin/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Admin Guide 3 | tags: 4 | - admin-guide 5 | icon: material/shield-outline 6 | --- 7 | 8 | # Admin Guide 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | The Admin Guide covers everything you need to know to use Tux on your Discord server. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/core/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core Concepts 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - core 7 | --- 8 | 9 | # Core Concepts 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section contains concepts that are used in the core of Tux. 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/content/admin/config/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Admin Configuration 3 | tags: 4 | - admin-guide 5 | - configuration 6 | --- 7 | 8 | # Admin Configuration 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains documentation for configuring Tux on your Discord server. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/developer/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developer Guide 3 | icon: material/folder-search-outline 4 | tags: 5 | - developer-guide 6 | --- 7 | 8 | # Developer Guide 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains documentation for developers working with Tux. 14 | 15 | 16 | -------------------------------------------------------------------------------- /typings/typer/colors.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | BLACK = ... 6 | RED = ... 7 | GREEN = ... 8 | YELLOW = ... 9 | BLUE = ... 10 | MAGENTA = ... 11 | CYAN = ... 12 | WHITE = ... 13 | RESET = ... 14 | BRIGHT_BLACK = ... 15 | BRIGHT_RED = ... 16 | BRIGHT_GREEN = ... 17 | BRIGHT_YELLOW = ... 18 | BRIGHT_BLUE = ... 19 | BRIGHT_MAGENTA = ... 20 | BRIGHT_CYAN = ... 21 | BRIGHT_WHITE = ... 22 | -------------------------------------------------------------------------------- /src/tux/ui/views/config/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tux Configuration UI Package. 3 | 4 | A modular, extensible configuration interface system using Discord Components V2. 5 | Provides a clean foundation for building configuration UIs with proper separation 6 | of concerns, reusable components, and type-safe interactions. 7 | """ 8 | 9 | from .dashboard import ConfigDashboard 10 | 11 | __all__ = [ 12 | "ConfigDashboard", 13 | ] 14 | -------------------------------------------------------------------------------- /docs/content/support/troubleshooting/developer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developer Troubleshooting 3 | tags: 4 | - support 5 | - troubleshooting 6 | - developer 7 | --- 8 | 9 | # Developer Troubleshooting 10 | 11 | Common issues developers encounter when contributing to Tux and their solutions. 12 | 13 | !!! warning "Work in progress" 14 | This section is a work in progress. Please help us by contributing to the documentation. 15 | -------------------------------------------------------------------------------- /docs/content/user/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: User Guide 3 | hide: 4 | - toc 5 | tags: 6 | - user-guide 7 | icon: octicons/person-24 8 | --- 9 | 10 | # User Guide 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | 15 | The User Guide covers everything you need to know to use Tux on your Discord server. 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/content/developer/best-practices/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developer Best Practices 3 | tags: 4 | - developer-guide 5 | - best-practices 6 | --- 7 | 8 | # Developer Best Practices 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains best practices for developers working with Tux. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/selfhost/config/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Self-Host Configuration 3 | tags: 4 | - selfhost 5 | - configuration 6 | --- 7 | 8 | # Self-Host Configuration 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | This section contains documentation for configuring Tux on your self-hosted instance. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/user/features/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Features 3 | tags: 4 | - user-guide 5 | - features 6 | --- 7 | 8 | # Features 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | Tux offers a wide range of features to enhance your Discord server. This section covers all the features available in Tux. 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/content/developer/best-practices/testing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing 3 | tags: 4 | - developer-guide 5 | - best-practices 6 | - testing 7 | --- 8 | 9 | # Testing 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | This section covers Tux's testing strategies and practices across different testing levels. 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/tux/modules/__init__.py: -------------------------------------------------------------------------------- 1 | """Tux bot modules package. 2 | 3 | This package contains all the feature modules for the Tux Discord bot. 4 | Each module is a self-contained package that provides specific functionality. 5 | 6 | This is a namespace package - individual modules are imported directly 7 | (e.g., from tux.modules.moderation import cases) rather than through this package. 8 | """ 9 | 10 | # Namespace package - no direct exports 11 | __all__ = [] 12 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/creating-first-cog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Creating Your First Cog 3 | description: How to create your first cog for Tux, including creating a new cog file, adding it to the bot, and testing it. 4 | tags: 5 | - developer-guide 6 | - tutorials 7 | - cogs 8 | --- 9 | 10 | # Creating Your First Cog 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /src/tux/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | """Custom plugins package for user-defined extensions. 2 | 3 | This package is intended for custom modules created by self-hosters. 4 | Modules placed here will be automatically discovered and loaded by the bot. 5 | 6 | This is a namespace package - individual plugins are imported directly 7 | (e.g., from tux.plugins.atl import fact) rather than through this package. 8 | """ 9 | 10 | # Namespace package - no direct exports 11 | __all__ = [] 12 | -------------------------------------------------------------------------------- /src/tux/__init__.py: -------------------------------------------------------------------------------- 1 | """Tux - The all in one discord bot for the All Things Linux Community. 2 | 3 | This package provides a comprehensive Discord bot with modular architecture, 4 | extensive functionality, and professional development practices. 5 | """ 6 | 7 | # Import the unified version system 8 | from tux.shared.version import get_version 9 | 10 | # Module-level version constant 11 | # Uses the unified version system for consistency 12 | __version__: str = get_version() 13 | -------------------------------------------------------------------------------- /typings/emojis/db/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from .db import Emoji 6 | from .utils import get_categories, get_emoji_aliases, get_emoji_by_alias, get_emoji_by_code, get_emojis_by_category, get_emojis_by_tag, get_tags 7 | 8 | ''' 9 | Emoji database. 10 | ''' 11 | __all__ = ['Emoji', 'get_emoji_aliases', 'get_emoji_by_code', 'get_emoji_by_alias', 'get_emojis_by_tag', 'get_emojis_by_category', 'get_tags', 'get_categories'] 12 | -------------------------------------------------------------------------------- /.trivyignore: -------------------------------------------------------------------------------- 1 | # Trivy ignore file for known issues that are being tracked separately 2 | # 3 | # CVE-2023-45853: Integer overflow in minizip (zlib) 4 | # Status: Marked as "will_not_fix" by upstream 5 | # Impact: Critical vulnerability in zlib library 6 | # Mitigation: This is a known issue in the Python base image 7 | # We are tracking this separately and investigating alternatives 8 | # TODO: Monitor for upstream fix or consider alternative base images 9 | CVE-2023-45853 10 | -------------------------------------------------------------------------------- /docs/content/developer/tutorials/creating-first-command.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Creating Your First Command 3 | description: How to create your first command for Tux, including creating a new command file, adding it to the bot, and testing it. 4 | tags: 5 | - developer-guide 6 | - tutorials 7 | - commands 8 | --- 9 | 10 | # Creating Your First Command 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | -------------------------------------------------------------------------------- /src/tux/services/hot_reload/cog.py: -------------------------------------------------------------------------------- 1 | """Hot reload cog for file watching and automatic reloading.""" 2 | 3 | from loguru import logger 4 | 5 | from tux.core.bot import Tux 6 | from tux.services.hot_reload.service import HotReload 7 | 8 | 9 | async def setup(bot: Tux) -> None: 10 | """Cog setup for hot reload. 11 | 12 | Parameters 13 | ---------- 14 | bot : Tux 15 | The bot instance. 16 | """ 17 | await bot.add_cog(HotReload(bot)) 18 | logger.debug("Hot reload cog loaded") 19 | -------------------------------------------------------------------------------- /src/tux/ui/views/__init__.py: -------------------------------------------------------------------------------- 1 | """View components for Discord UI interactions. 2 | 3 | This module contains reusable view components for complex Discord interactions. 4 | """ 5 | 6 | from tux.ui.views.confirmation import ( 7 | BaseConfirmationView, 8 | ConfirmationDanger, 9 | ConfirmationNormal, 10 | ) 11 | from tux.ui.views.tldr import TldrPaginatorView 12 | 13 | __all__ = [ 14 | "BaseConfirmationView", 15 | "ConfirmationDanger", 16 | "ConfirmationNormal", 17 | "TldrPaginatorView", 18 | ] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: Discord Community Support 5 | url: https://discord.gg/gpmSjcjQxg 6 | about: Get help from the community in real-time 7 | - name: Documentation 8 | url: https://tux.atl.dev 9 | about: Check the documentation for guides and API reference 10 | - name: Feature Requests 11 | url: https://github.com/allthingslinux/tux/issues/new?template=feature_request.md 12 | about: Suggest a new feature or enhancement 13 | -------------------------------------------------------------------------------- /docs/content/selfhost/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Self-Hoster Guide 3 | tags: 4 | - selfhost 5 | icon: material/server-outline 6 | --- 7 | 8 | # Self-Hoster Guide 9 | 10 | Get started by reading the [Installation Guide](./install/index.md) to set up Tux on your own server. 11 | After installation, refer to the [Configuration Guide](./config/index.md) to customize Tux to your needs. 12 | For ongoing maintenance and operations, the [Management Guide](./manage/index.md) provides essential information. 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you find any form of Vulnerability within Tux or it's subsystems and tools please see our security policy and report it accordingly, the policy can be found at https://allthingslinux.org/security. 6 | 7 | ## Supported Versions 8 | 9 | Currently only the latest stable release will be supported with security updates, no other release is included in our security policy. It is your responsibility to keep your Tux instance up to date to ensure it is secure. 10 | -------------------------------------------------------------------------------- /typings/typer/testing.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from typing import Any, IO, Mapping, Optional, Sequence, Union 6 | from click.testing import CliRunner as ClickCliRunner, Result 7 | from typer.main import Typer 8 | 9 | class CliRunner(ClickCliRunner): 10 | def invoke(self, app: Typer, args: Optional[Union[str, Sequence[str]]] = ..., input: Optional[Union[bytes, str, IO[Any]]] = ..., env: Optional[Mapping[str, str]] = ..., catch_exceptions: bool = ..., color: bool = ..., **extra: Any) -> Result: 11 | ... 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /typings/py_pglite/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from .clients import AsyncpgClient, PsycopgClient, get_client, get_default_client 6 | from .config import PGliteConfig 7 | from .manager import PGliteManager 8 | 9 | """py-pglite: Python testing library for PGlite integration. 10 | 11 | Provides seamless integration between PGlite (in-memory PostgreSQL) 12 | and Python test suites with support for SQLAlchemy, SQLModel, and Django. 13 | """ 14 | __version__ = ... 15 | __all__ = ["PGliteConfig", "PGliteManager", "get_client", "get_default_client", "PsycopgClient", "AsyncpgClient"] 16 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import {}, self ? null }: 2 | 3 | pkgs.mkShell { 4 | buildInputs = if self == null then [] else [ 5 | self.packages.${pkgs.system}.envrc 6 | ]; 7 | 8 | packages = with pkgs; [ 9 | python313 10 | uv 11 | git 12 | jq 13 | ]; 14 | 15 | shellHook = '' 16 | # See perSystem.packages.envrc 17 | if command -v envrc >/dev/null 2>&1; then 18 | envrc 19 | fi 20 | 21 | # Enters the user's preferred shell using a more robust method 22 | $(getent passwd $(id -un) | cut -d: -f7 | tr -d '\n') 23 | 24 | # Exits after child shell exits 25 | exit 26 | ''; 27 | } 28 | -------------------------------------------------------------------------------- /src/tux/services/moderation/__init__.py: -------------------------------------------------------------------------------- 1 | """Moderation services for Tux Bot such as case service, communication service and execution service.""" 2 | 3 | from .case_service import CaseService 4 | from .communication_service import CommunicationService 5 | from .execution_service import ExecutionService 6 | from .factory import ModerationServiceFactory 7 | from .moderation_coordinator import ModerationCoordinator 8 | 9 | __all__ = [ 10 | # Core services 11 | "CaseService", 12 | "CommunicationService", 13 | "ExecutionService", 14 | # Coordination and factory 15 | "ModerationCoordinator", 16 | "ModerationServiceFactory", 17 | ] 18 | -------------------------------------------------------------------------------- /src/tux/shared/config/generators/__init__.py: -------------------------------------------------------------------------------- 1 | """Configuration generators package. 2 | 3 | This package provides custom generators for pydantic-settings-export 4 | to generate configuration files in various formats. 5 | 6 | Note: TOML and Markdown generation use the built-in generators from pydantic-settings-export. 7 | """ 8 | 9 | from .base import camel_to_snake 10 | from .json import JsonGenerator, JsonGeneratorSettings 11 | from .yaml import YamlGenerator, YamlGeneratorSettings 12 | 13 | __all__ = [ 14 | "JsonGenerator", 15 | "JsonGeneratorSettings", 16 | "YamlGenerator", 17 | "YamlGeneratorSettings", 18 | "camel_to_snake", 19 | ] 20 | -------------------------------------------------------------------------------- /src/tux/shared/config/generators/base.py: -------------------------------------------------------------------------------- 1 | """Shared utilities for configuration generators.""" 2 | 3 | import re 4 | 5 | 6 | def camel_to_snake(name: str) -> str: 7 | """Convert CamelCase to snake_case. 8 | 9 | Parameters 10 | ---------- 11 | name : str 12 | CamelCase string 13 | 14 | Returns 15 | ------- 16 | str 17 | snake_case string 18 | 19 | """ 20 | # Insert underscore before uppercase letters (except at start) 21 | s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) 22 | # Insert underscore before uppercase letters preceded by lowercase 23 | return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() 24 | -------------------------------------------------------------------------------- /typings/py_pglite/sqlalchemy/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from .fixtures import pglite_engine, pglite_session, pglite_sqlalchemy_engine, pglite_sqlalchemy_session 6 | from .manager import SQLAlchemyPGliteManager 7 | from .utils import create_all_tables, drop_all_tables, get_session_class 8 | 9 | """SQLAlchemy integration for py-pglite. 10 | 11 | This module provides SQLAlchemy-specific fixtures and utilities for py-pglite. 12 | """ 13 | __all__ = ["SQLAlchemyPGliteManager", "pglite_engine", "pglite_session", "pglite_sqlalchemy_session", "pglite_sqlalchemy_engine", "create_all_tables", "drop_all_tables", "get_session_class"] 14 | -------------------------------------------------------------------------------- /src/tux/modules/config/overview.py: -------------------------------------------------------------------------------- 1 | """Configuration overview commands using unified dashboard.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | from discord.ext import commands 8 | 9 | from .base import BaseConfigManager 10 | 11 | if TYPE_CHECKING: 12 | from tux.core.bot import Tux 13 | 14 | 15 | class ConfigOverview(BaseConfigManager): 16 | """Overview and status commands for config system using unified dashboard.""" 17 | 18 | async def overview_command(self, ctx: commands.Context[Tux]) -> None: 19 | """Launch the unified configuration dashboard.""" 20 | await self.configure_dashboard(ctx, "overview") 21 | -------------------------------------------------------------------------------- /typings/typer/completion.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import click 6 | from typing import Any, MutableMapping, Tuple 7 | from .models import ParamMeta 8 | 9 | _click_patched = ... 10 | def get_completion_inspect_parameters() -> Tuple[ParamMeta, ParamMeta]: 11 | ... 12 | 13 | def install_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 14 | ... 15 | 16 | def show_callback(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 17 | ... 18 | 19 | def shell_complete(cli: click.Command, ctx_args: MutableMapping[str, Any], prog_name: str, complete_var: str, instruction: str) -> int: 20 | ... 21 | 22 | -------------------------------------------------------------------------------- /typings/reactionmenu/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from .abc import Page 6 | from .buttons import ReactionButton, ViewButton 7 | from .core import ReactionMenu 8 | from .views_menu import ViewMenu, ViewSelect 9 | 10 | """ 11 | reactionmenu • discord pagination 12 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 | 14 | A library to create a discord.py 2.0+ paginator. Supports pagination with buttons, reactions, and category selection using selects. 15 | 16 | :copyright: (c) 2021-present @defxult 17 | :license: MIT 18 | 19 | """ 20 | __source__ = ... 21 | __all__ = ("ReactionMenu", "ReactionButton", "ViewMenu", "ViewButton", "ViewSelect", "Page") 22 | -------------------------------------------------------------------------------- /docs/content/developer/best-practices/caching.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Caching Best Practices 3 | description: Caching best practices for Tux development, including cache invalidation, cache expiration, and cache eviction. 4 | tags: 5 | - developer-guide 6 | - best-practices 7 | - caching 8 | --- 9 | 10 | ## Caching Best Practices 11 | 12 | !!! warning "Work in progress" 13 | This section is a work in progress. Please help us by contributing to the documentation. 14 | 15 | Caching has not been implemented yet, so this section is placeholder. In the future, Redis or Valkey will be used for caching. Some patterns of caching have been implemented across the codebase for smaller use cases but they are not yet properly documented. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | labels: "type: feature-request" 5 | assignees: '' 6 | 7 | --- 8 | # Feature Request 9 | 10 | ## Is your feature request related to a problem? Please describe 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | ## Describe the solution you'd like 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | ## Describe alternatives you've considered 19 | 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | ## Additional Context 23 | 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /src/tux/database/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """ 2 | Revision ID: ${up_revision} 3 | Revises: ${down_revision | comma,n} 4 | Create Date: ${create_date} 5 | """ 6 | from __future__ import annotations 7 | 8 | from typing import Sequence, Union 9 | 10 | from alembic import op 11 | import sqlalchemy as sa 12 | import sqlmodel 13 | ${imports if imports else ""} 14 | 15 | # revision identifiers, used by Alembic. 16 | revision: str = ${repr(up_revision)} 17 | down_revision: Union[str, None] = ${repr(down_revision)} 18 | branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} 19 | depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} 20 | 21 | 22 | def upgrade() -> None: 23 | ${upgrades if upgrades else "pass"} 24 | 25 | 26 | def downgrade() -> None: 27 | ${downgrades if downgrades else "pass"} 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question about Tux 4 | labels: "type: question" 5 | assignees: '' 6 | 7 | --- 8 | 9 | # Question 10 | 11 | ## What would you like to know? 12 | 13 | A clear and concise description of your question. 14 | 15 | ## Context 16 | 17 | Please provide any relevant context about your question, such as: 18 | 19 | - What you're trying to accomplish 20 | - What you've already tried 21 | - Any relevant code snippets or configuration 22 | 23 | ## Additional Information 24 | 25 | - Tux version: [e.g., v0.1.0] 26 | - Python version: [e.g., 3.13.2] 27 | - OS: [e.g., Ubuntu 22.04] 28 | 29 | ## Where to Get Help 30 | 31 | If this is a quick question, you might get faster help in our [Discord community](https://discord.gg/gpmSjcjQxg). 32 | 33 | For documentation, check out [tux.atl.dev](https://tux.atl.dev). 34 | -------------------------------------------------------------------------------- /docker/adminer/index.php: -------------------------------------------------------------------------------- 1 | getenv('ADMINER_DEFAULT_SERVER') ?: 'tux-postgres', 15 | 'username' => getenv('ADMINER_DEFAULT_USERNAME') ?: 'tuxuser', 16 | 'password' => getenv('ADMINER_DEFAULT_PASSWORD') ?: 'ChangeThisToAStrongPassword123!', 17 | 'driver' => getenv('ADMINER_DEFAULT_DRIVER') ?: 'pgsql', 18 | 'db' => getenv('ADMINER_DEFAULT_DB') ?: 'tuxdb', 19 | ]; 20 | } 21 | 22 | // Include the main Adminer application 23 | include './adminer.php'; 24 | -------------------------------------------------------------------------------- /src/tux/core/__init__.py: -------------------------------------------------------------------------------- 1 | """Core module for Tux bot. 2 | 3 | This module provides the core infrastructure including: 4 | - Main bot class (Tux) 5 | - Base cog class for extensions 6 | - Command prefix resolution 7 | - Permission system and decorators 8 | - Common converters and utilities 9 | """ 10 | 11 | from tux.core.app import TuxApp, get_prefix 12 | from tux.core.base_cog import BaseCog 13 | from tux.core.bot import Tux 14 | from tux.core.checks import requires_command_permission 15 | from tux.core.converters import get_channel_safe 16 | from tux.core.permission_system import DEFAULT_RANKS, get_permission_system 17 | 18 | __all__ = [ 19 | # Core classes 20 | "BaseCog", 21 | "Tux", 22 | "TuxApp", 23 | # Functions 24 | "get_prefix", 25 | "get_channel_safe", 26 | # Permission system 27 | "requires_command_permission", 28 | "get_permission_system", 29 | "DEFAULT_RANKS", 30 | ] 31 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | CLI Infrastructure Package. 3 | 4 | This package provides a clean, object-oriented foundation for building CLI applications 5 | with proper separation of concerns and extensibility. 6 | """ 7 | 8 | from scripts.base import BaseCLI 9 | from scripts.config import ConfigCLI 10 | from scripts.db import DatabaseCLI 11 | from scripts.dev import DevCLI 12 | from scripts.docker_cli import DockerCLI 13 | from scripts.docs import DocsCLI 14 | from scripts.registry import Command, CommandGroup, CommandRegistry 15 | from scripts.rich_utils import RichCLI 16 | from scripts.test import TestCLI 17 | from scripts.tux import TuxCLI 18 | 19 | __all__ = [ 20 | "BaseCLI", 21 | "Command", 22 | "CommandGroup", 23 | "CommandRegistry", 24 | "ConfigCLI", 25 | "DatabaseCLI", 26 | "DevCLI", 27 | "DockerCLI", 28 | "DocsCLI", 29 | "RichCLI", 30 | "TestCLI", 31 | "TuxCLI", 32 | ] 33 | -------------------------------------------------------------------------------- /.github/scripts/docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Docs workflow helper scripts 3 | # Usage: docs.sh [args...] 4 | 5 | set -euo pipefail 6 | 7 | # shellcheck disable=SC2034 8 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | 10 | # Prepare coverage reports for MkDocs 11 | prepare_coverage() { 12 | echo "Preparing coverage reports for mkdocs-coverage plugin..." 13 | 14 | if [ -d "htmlcov" ] && [ "$(ls -A htmlcov)" ]; then 15 | cp -r htmlcov docs/ 16 | echo "Copied htmlcov from tests workflow to docs/htmlcov for mkdocs-coverage plugin" 17 | else 18 | echo "ERROR: Coverage data not found after tests workflow completed" 19 | exit 1 20 | fi 21 | } 22 | 23 | COMMAND="${1:-}" 24 | shift || true 25 | 26 | case "$COMMAND" in 27 | prepare-coverage) 28 | prepare_coverage "$@" 29 | ;; 30 | *) 31 | echo "Usage: docs.sh {prepare-coverage} [args...]" 32 | exit 1 33 | ;; 34 | esac 35 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | # Exclude YAML configuration files that contain markdown-like comments 2 | .yamlint 3 | 4 | # Exclude other configuration files that might have markdown-like syntax 5 | .codecov.yml 6 | .pre-commit-config.yaml 7 | compose.yaml 8 | *.lock 9 | 10 | # Exclude auto-generated files 11 | CONFIG.md 12 | 13 | # Exclude build and cache directories 14 | .venv/ 15 | .pytest_cache/ 16 | .ruff_cache/ 17 | __pycache__/ 18 | .cache/ 19 | htmlcov/ 20 | 21 | # Exclude archived and temporary files 22 | .archive/ 23 | logs/ 24 | 25 | # Exclude development environments 26 | .devcontainer/ 27 | .vscode/ 28 | .cursor/ 29 | 30 | # Exclude dependencies and generated files 31 | prisma/ 32 | typings/ 33 | 34 | .github/ 35 | 36 | .kiro/ 37 | 38 | .audit/ 39 | 40 | # Project-specific ignores 41 | sqlmodel-refactor/** 42 | docs/db/README.md 43 | 44 | .archive 45 | .archive/** 46 | 47 | docs/content/reference/configuration.md 48 | docs/includes/abbreviations.md 49 | -------------------------------------------------------------------------------- /src/tux/ui/__init__.py: -------------------------------------------------------------------------------- 1 | """UI components for the Tux Discord bot. 2 | 3 | This module contains all user interface components including: 4 | - Embeds and embed creators 5 | - Buttons and interactive components 6 | - Views for complex interactions 7 | - Modals for user input 8 | - Help system components 9 | """ 10 | 11 | from tux.ui.buttons import GithubButton, XkcdButtons 12 | from tux.ui.embeds import EmbedCreator, EmbedType 13 | from tux.ui.modals import ReportModal 14 | from tux.ui.views import ( 15 | BaseConfirmationView, 16 | ConfirmationDanger, 17 | ConfirmationNormal, 18 | TldrPaginatorView, 19 | ) 20 | 21 | __all__ = [ 22 | # Embeds 23 | "EmbedCreator", 24 | "EmbedType", 25 | # Buttons 26 | "GithubButton", 27 | "XkcdButtons", 28 | # Views 29 | "BaseConfirmationView", 30 | "ConfirmationDanger", 31 | "ConfirmationNormal", 32 | "TldrPaginatorView", 33 | # Modals 34 | "ReportModal", 35 | ] 36 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Environment files 2 | .env* 3 | !.env.example 4 | 5 | # Python virtual environment and caches 6 | .venv/ 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | .pytest_cache/ 11 | .coverage 12 | .mypy_cache/ 13 | .ruff_cache/ 14 | 15 | # Build artifacts 16 | build/ 17 | dist/ 18 | *.egg-info/ 19 | .eggs/ 20 | 21 | # IDE/Editor files 22 | .vscode/ 23 | .idea/ 24 | *.swp 25 | *.swo 26 | *~ 27 | .DS_Store 28 | 29 | # Documentation and development files 30 | docs-build/ 31 | site/ 32 | *.md 33 | !README.md 34 | !LICENSE 35 | !requirements.md 36 | 37 | # Development configuration 38 | .cursorrules 39 | .editorconfig 40 | .pre-commit-config.yaml 41 | 42 | # Logs 43 | *.log 44 | logs/ 45 | 46 | # Git (now excluded since version info is passed via build args) 47 | # This improves build performance and security 48 | .git/ 49 | .gitignore 50 | .gitattributes 51 | 52 | # Docker files (prevent recursive inclusion) 53 | Containerfile* 54 | compose.yaml 55 | .dockerignore 56 | 57 | # Cache directories 58 | .cache/ 59 | -------------------------------------------------------------------------------- /src/tux/modules/config/commands.py: -------------------------------------------------------------------------------- 1 | """Command permission management for the config system.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | from discord.ext import commands 8 | 9 | from .base import BaseConfigManager 10 | 11 | if TYPE_CHECKING: 12 | from tux.core.bot import Tux 13 | 14 | 15 | class CommandManager(BaseConfigManager): 16 | """Management commands for command permissions.""" 17 | 18 | async def configure_commands(self, ctx: commands.Context[Tux]) -> None: 19 | """ 20 | Configure command permissions using the unified config dashboard. 21 | 22 | This command launches the unified configuration dashboard in commands mode 23 | to allow administrators to assign permission ranks to moderation commands. 24 | 25 | Parameters 26 | ---------- 27 | ctx : commands.Context[Tux] 28 | The context of the command invocation. 29 | """ 30 | await self.configure_dashboard(ctx, "commands") 31 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/settings.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from pathlib import Path 6 | from pydantic import BaseModel, model_validator 7 | from pydantic_settings_export.sources import TomlSettings 8 | 9 | __all__ = ("RelativeToSettings", "PSESettings") 10 | class RelativeToSettings(BaseModel): 11 | """Settings for the relative directory.""" 12 | model_config = ... 13 | replace_abs_paths: bool = ... 14 | alias: str = ... 15 | 16 | 17 | class PSESettings(TomlSettings): 18 | """Global settings for pydantic_settings_export.""" 19 | model_config = ... 20 | default_settings: list[str] = ... 21 | root_dir: Path = ... 22 | project_dir: Path = ... 23 | relative_to: RelativeToSettings = ... 24 | respect_exclude: bool = ... 25 | @classmethod 26 | @model_validator(mode="before") 27 | def default_for_root_dir(cls, data: dict) -> dict: 28 | """Set the default value for root_dir if not set.""" 29 | ... 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | labels: "type: bug" 5 | assignees: '' 6 | 7 | --- 8 | 9 | # Bug Report 10 | 11 | ## Describe the Bug 12 | 13 | A clear and concise description of what the bug is. 14 | 15 | ## To Reproduce 16 | 17 | Steps to reproduce the behavior: 18 | 19 | 1. Go to '...' 20 | 2. Click on '....' 21 | 3. Scroll down to '....' 22 | 4. See error 23 | 24 | Please include code snippets or screenshots where applicable. 25 | 26 | ## Expected Behavior 27 | 28 | A clear and concise description of what you expected to happen. 29 | 30 | ## Screenshots 31 | 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | ## Environment (please complete the following information) 35 | 36 | - OS: [e.g. Ubuntu 20.04] 37 | - Python version: [e.g. Python 3.12] 38 | - Discord.py version: [e.g. 2.0.0] 39 | - Tux version: [e.g. v1.0.0] 40 | 41 | ## Additional Context 42 | 43 | Add any other context about the problem here, such as logs, configuration files, etc. 44 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "EditorConfig.EditorConfig", 4 | "ms-vscode-remote.remote-containers", 5 | "ms-python.python", 6 | "detachhead.basedpyright", 7 | "ms-azuretools.vscode-docker", 8 | "charliermarsh.ruff", 9 | "kevinrose.vsc-python-indent", 10 | "mikestead.dotenv", 11 | "njpwerner.autodocstring", 12 | "magicstack.MagicPython", 13 | "usernamehw.errorlens", 14 | "sourcery.sourcery", 15 | "redhat.vscode-yaml", 16 | "ryanluker.vscode-coverage-gutters", 17 | "ms-azuretools.vscode-containers", 18 | "tamasfe.even-better-toml", 19 | "DavidAnson.vscode-markdownlint", 20 | "ms-python.debugpy", 21 | "timonwong.shellcheck", 22 | "mkhl.shfmt" 23 | ], 24 | "unwantedRecommendations": [ 25 | "ms-python.pylint", 26 | "ms-python.autopep8", 27 | "ms-python.black-formatter", 28 | "ms-python.isort", 29 | "ms-python.pylint", 30 | "ms-python.flake8", 31 | "ms-python.mypy-type-checker", 32 | "ms-python.vscode-pylance" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /src/tux/modules/config/logs.py: -------------------------------------------------------------------------------- 1 | """Log channel configuration management using unified dashboard.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | from discord.ext import commands 8 | 9 | from .base import BaseConfigManager 10 | 11 | if TYPE_CHECKING: 12 | from tux.core.bot import Tux 13 | 14 | 15 | class LogManager(BaseConfigManager): 16 | """Management commands for log channel configuration using unified dashboard.""" 17 | 18 | async def configure_logs(self, ctx: commands.Context[Tux]) -> None: 19 | """ 20 | Configure log channel assignments using the unified config dashboard. 21 | 22 | This command launches the unified configuration dashboard in logs mode 23 | to allow administrators to assign text channels for various bot logging purposes. 24 | 25 | Parameters 26 | ---------- 27 | ctx : commands.Context[Tux] 28 | The context of the command invocation. 29 | """ 30 | await self.configure_dashboard(ctx, "logs") 31 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | document-start: enable 5 | 6 | # Allow longer lines for readability in configuration files 7 | line-length: 8 | max: 185 9 | level: warning 10 | 11 | # Allow empty values in mappings (common in Docker Compose) 12 | empty-values: 13 | forbid-in-block-mappings: false 14 | forbid-in-flow-mappings: false 15 | 16 | # Be more lenient with indentation for nested structures 17 | indentation: 18 | spaces: 2 19 | indent-sequences: true 20 | check-multi-line-strings: false 21 | truthy: 22 | check-keys: false 23 | 24 | # Allow comments to start anywhere 25 | comments-indentation: disable 26 | 27 | # Allow trailing spaces in empty lines 28 | empty-lines: 29 | max-start: 1 30 | max-end: 1 31 | max: 2 32 | 33 | # Allow dashes in key names (common in GitHub Actions) 34 | key-duplicates: enable 35 | 36 | # Allow brackets in flow sequences 37 | brackets: enable 38 | 39 | # Allow braces in flow mappings 40 | braces: enable 41 | comments: 42 | min-spaces-from-content: 1 43 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/sources.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from pydantic_settings import BaseSettings 6 | from pydantic_settings.sources import PydanticBaseSettingsSource 7 | 8 | __all__ = ("TomlSettings", ) 9 | class TomlSettings(BaseSettings): 10 | """The sources mixin.""" 11 | @classmethod 12 | def settings_customise_sources(cls, settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource) -> tuple[PydanticBaseSettingsSource, ...]: 13 | """Customise the sources. 14 | 15 | :param settings_cls: The settings class. 16 | :param init_settings: The init settings source. 17 | :param env_settings: The env settings source. 18 | :param dotenv_settings: The dotenv settings source. 19 | :param file_secret_settings: The file secret settings source. 20 | :return: The customised sources. 21 | """ 22 | ... 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/scripts/shared.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Shared utility functions for GitHub Actions scripts 3 | # Usage: shared.sh [args...] 4 | 5 | set -euo pipefail 6 | 7 | # shellcheck disable=SC2034 8 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | 10 | # Calculate size in GB from bytes 11 | calculate_size_gb() { 12 | local bytes="${1:-0}" 13 | python3 -c "import sys; print(f'{int(sys.argv[1]) / 1024 / 1024 / 1024:.2f}')" "$bytes" 14 | } 15 | 16 | # Compare size in GB against threshold 17 | compare_size() { 18 | local size_gb="${1:-0}" 19 | local threshold="${2:-${REGISTRY_SIZE_WARNING_GB:-5}}" 20 | python3 -c "import sys; size = float(sys.argv[1]); threshold = float(sys.argv[2]); print('true' if size > threshold else 'false')" "$size_gb" "$threshold" 21 | } 22 | 23 | COMMAND="${1:-}" 24 | shift || true 25 | 26 | case "$COMMAND" in 27 | calculate-size-gb) 28 | calculate_size_gb "$@" 29 | ;; 30 | compare-size) 31 | compare_size "$@" 32 | ;; 33 | *) 34 | echo "Usage: shared.sh {calculate-size-gb|compare-size} [args...]" 35 | exit 1 36 | ;; 37 | esac 38 | -------------------------------------------------------------------------------- /.docstr.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # paths: # list or string 3 | # - docstr_coverage 4 | # badge: docs # Path 5 | # exclude: .*/test # regex 6 | # verbose: 3 # int (0-4) 7 | # skip_magic: True # Boolean 8 | # skip_file_doc: True # Boolean 9 | # skip_init: True # Boolean 10 | # skip_class_def: True # Boolean 11 | # skip_private: True # Boolean 12 | # follow_links: True # Boolean 13 | # accept_empty: True # Boolean 14 | # ignore_names_file: .*/test # regex 15 | # fail_under: 90 # int 16 | # percentage_only: True # Boolean 17 | # ignore_patterns: # Dict with key/value pairs of file-pattern/node-pattern 18 | # .*: method_to_ignore_in_all_files 19 | # FileWhereWeWantToIgnoreAllSpecialMethods: "__.+__" 20 | # SomeFile: 21 | # - method_to_ignore1 22 | # - method_to_ignore2 23 | # - method_to_ignore3 24 | # a_very_important_view_file: 25 | # - "^get$" 26 | # - "^set$" 27 | # - "^post$" 28 | # detect_.*: 29 | # - "get_val.*" 30 | exclude: (?:(?:^|.*/)(?:\.venv|examples|\.archive|typings|tests|\.kiro|\.audit)(?:/|$))|(?:(?:^|.*/)src/tux/database/migrations(?:/versions)?(?:/|$))|(?:(?:^|.*/)migrations(?:/|$)) 31 | -------------------------------------------------------------------------------- /typings/typer/__init__.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from shutil import get_terminal_size as get_terminal_size 6 | from click.exceptions import Abort as Abort, BadParameter as BadParameter, Exit as Exit 7 | from click.termui import clear as clear, confirm as confirm, echo_via_pager as echo_via_pager, edit as edit, getchar as getchar, pause as pause, progressbar as progressbar, prompt as prompt, secho as secho, style as style, unstyle as unstyle 8 | from click.utils import echo as echo, format_filename as format_filename, get_app_dir as get_app_dir, get_binary_stream as get_binary_stream, get_text_stream as get_text_stream, open_file as open_file 9 | from . import colors as colors 10 | from .main import Typer as Typer, launch as launch, run as run 11 | from .models import CallbackParam as CallbackParam, Context as Context, FileBinaryRead as FileBinaryRead, FileBinaryWrite as FileBinaryWrite, FileText as FileText, FileTextWrite as FileTextWrite 12 | from .params import Argument as Argument, Option as Option 13 | 14 | """Typer, build great CLIs. Easy to code. Based on Python type hints.""" 15 | __version__ = ... 16 | -------------------------------------------------------------------------------- /docs/content/developer/concepts/database/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Database Concepts 3 | tags: 4 | - developer-guide 5 | - concepts 6 | - database 7 | --- 8 | 9 | # Database Concepts 10 | 11 | !!! warning "Work in progress" 12 | This section is a work in progress. Please help us by contributing to the documentation. 13 | 14 | ## Overview 15 | 16 | Tux uses a robust, async-first PostgreSQL database layer built with SQLModel and SQLAlchemy. The database architecture follows a clean three-layer pattern that separates concerns and enables maintainable, type-safe database operations. 17 | 18 | The three layers work together to provide: 19 | 20 | - **Service Layer**: Connection management, session handling, and health monitoring 21 | - **Controller Layer**: Business logic, query optimization, and specialized operations 22 | - **Model Layer**: Type-safe data models with automatic serialization and relationships 23 | 24 | This architecture supports complex Discord bot operations including moderation, user leveling, custom commands, and configuration management, all while maintaining excellent performance and developer experience. 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/content/assets/stylesheets/typography.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, sans-serif; 3 | font-feature-settings: 'liga' 1, 'calt' 1, 'ss02' 1; /* fix for Chrome */ 4 | --md-text-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", "Arial", sans-serif; 5 | --md-code-font: "JetBrains Mono", "SF Mono", "Monaco", "Inconsolata", "Fira Code", "Droid Sans Mono", "Courier New", monospace; 6 | } 7 | 8 | /* Use Inter Variable font if supported */ 9 | @supports (font-variation-settings: normal) { 10 | :root { 11 | font-family: InterVariable, sans-serif; 12 | --md-text-font: "InterVariable", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", "Arial", sans-serif; 13 | /* Variable font features - same as static Inter */ 14 | font-feature-settings: 'liga' 1, 'calt' 1, 'ss02' 1; 15 | } 16 | } 17 | 18 | body { 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | * { 24 | text-rendering: auto; 25 | font-feature-settings: "kern" 1; 26 | font-kerning: normal; 27 | font-optical-sizing: none; 28 | text-rendering: optimizeLegibility; 29 | } 30 | -------------------------------------------------------------------------------- /typings/typer/_completion_shared.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from enum import Enum 6 | from pathlib import Path 7 | from typing import Optional, Tuple 8 | 9 | class Shells(str, Enum): 10 | bash = ... 11 | zsh = ... 12 | fish = ... 13 | powershell = ... 14 | pwsh = ... 15 | 16 | 17 | COMPLETION_SCRIPT_BASH = ... 18 | COMPLETION_SCRIPT_ZSH = ... 19 | COMPLETION_SCRIPT_FISH = ... 20 | COMPLETION_SCRIPT_POWER_SHELL = ... 21 | _completion_scripts = ... 22 | _invalid_ident_char_re = ... 23 | def get_completion_script(*, prog_name: str, complete_var: str, shell: str) -> str: 24 | ... 25 | 26 | def install_bash(*, prog_name: str, complete_var: str, shell: str) -> Path: 27 | ... 28 | 29 | def install_zsh(*, prog_name: str, complete_var: str, shell: str) -> Path: 30 | ... 31 | 32 | def install_fish(*, prog_name: str, complete_var: str, shell: str) -> Path: 33 | ... 34 | 35 | def install_powershell(*, prog_name: str, complete_var: str, shell: str) -> Path: 36 | ... 37 | 38 | def install(shell: Optional[str] = ..., prog_name: Optional[str] = ..., complete_var: Optional[str] = ...) -> Tuple[str, Path]: 39 | ... 40 | 41 | -------------------------------------------------------------------------------- /.devcontainer/post-create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "🚀 Setting up Tux development environment..." 5 | 6 | # Install uv if not already installed 7 | if ! command -v uv > /dev/null 2>&1; then 8 | echo "📦 Installing uv..." 9 | curl -LsSf https://astral.sh/uv/install.sh | sh 10 | export PATH="$HOME/.cargo/bin:$PATH" 11 | 12 | # Add to bashrc for persistence 13 | # Use single quotes to write literal $HOME and $PATH (they'll expand when bashrc is sourced) 14 | # shellcheck disable=SC2016 15 | echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc 16 | fi 17 | 18 | # Verify uv installation 19 | if command -v uv > /dev/null 2>&1; then 20 | echo "✅ uv installed: $(uv --version)" 21 | else 22 | echo "❌ Failed to install uv" 23 | exit 1 24 | fi 25 | 26 | # Sync all dependencies 27 | echo "📥 Installing dependencies..." 28 | uv sync --all-groups 29 | 30 | # Verify Python environment 31 | echo "🐍 Python environment:" 32 | python --version 33 | which python 34 | 35 | # Verify imports work 36 | echo "🔍 Verifying Python path..." 37 | python -c "import sys; print('Python path:'); [print(f' {p}') for p in sys.path if 'tux' in p.lower() or 'src' in p.lower()]" 38 | 39 | echo "✅ Development environment setup complete!" 40 | -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block extrahead %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /src/tux/core/checks.py: -------------------------------------------------------------------------------- 1 | """Dynamic permission system with database-driven command access control. 2 | 3 | This module provides permission decorators and system initialization functions 4 | for enforcing command-level permissions based on database configuration. All 5 | permission requirements are stored in the database and evaluated per-guild at 6 | runtime. Commands are denied by default if no explicit permission is configured. 7 | 8 | The permission system is initialized during bot setup and provides decorators 9 | for command access control. Guild administrators configure permissions through 10 | the bot's configuration commands. 11 | """ 12 | 13 | # Dynamic permission decorator 14 | from tux.core.decorators import requires_command_permission 15 | 16 | # Core permission system functions 17 | from tux.core.permission_system import ( 18 | get_permission_system, 19 | init_permission_system, 20 | ) 21 | 22 | # Permission exceptions 23 | from tux.shared.exceptions import TuxPermissionDeniedError 24 | 25 | __all__ = [ 26 | # Exceptions 27 | "TuxPermissionDeniedError", 28 | # Core functions 29 | "get_permission_system", 30 | "init_permission_system", 31 | # The ONLY decorator - 100% dynamic 32 | "requires_command_permission", 33 | ] 34 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/generators/simple.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from pydantic_settings_export.models import SettingsInfoModel 6 | from .abstract import AbstractGenerator, BaseGeneratorSettings 7 | 8 | __all__ = ("SimpleGenerator", ) 9 | INDENT_CHAR = ... 10 | HEADER_UNDERLINE_CHAR = ... 11 | class SimpleSettings(BaseGeneratorSettings): 12 | """Settings for the simple generator.""" 13 | model_config = ... 14 | 15 | 16 | class SimpleGenerator(AbstractGenerator[SimpleSettings]): 17 | """The Simple generator.""" 18 | name = ... 19 | config = SimpleSettings 20 | def generate_single(self, settings_info: SettingsInfoModel, level: int = ...) -> str: 21 | """Generate simple text documentation for settings. 22 | 23 | Produces a clean, readable text format with: 24 | - Section headers with underlines 25 | - Field descriptions and types 26 | - Default values and examples 27 | - Proper spacing and indentation 28 | 29 | :param settings_info: Settings model to document. 30 | :param level: Nesting level for indentation. 31 | :return: Formatted text documentation with consistent styling. 32 | """ 33 | ... 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/fixtures/__init__.py: -------------------------------------------------------------------------------- 1 | """Test fixtures package. 2 | 3 | This package contains all test fixtures organized by category: 4 | - database_fixtures: Database and PGlite-related fixtures 5 | - test_data_fixtures: Sample data fixtures and test constants 6 | - sentry_fixtures: Sentry and Discord mock fixtures 7 | 8 | Fixtures are automatically discovered by pytest when imported in conftest.py. 9 | """ 10 | 11 | # Import modules to register fixtures with pytest 12 | # These imports are for side effects (pytest fixture registration) 13 | from . import database_fixtures # noqa: F401 14 | from . import test_data_fixtures # noqa: F401 15 | from . import sentry_fixtures # noqa: F401 16 | 17 | # Export test constants and utility functions 18 | from .test_data_fixtures import ( 19 | TEST_CHANNEL_ID, 20 | TEST_GUILD_ID, 21 | TEST_MODERATOR_ID, 22 | TEST_USER_ID, 23 | validate_guild_config_structure, 24 | validate_guild_structure, 25 | validate_relationship_integrity, 26 | ) 27 | 28 | __all__ = [ 29 | # Test constants 30 | "TEST_CHANNEL_ID", 31 | "TEST_GUILD_ID", 32 | "TEST_MODERATOR_ID", 33 | "TEST_USER_ID", 34 | # Validation functions 35 | "validate_guild_config_structure", 36 | "validate_guild_structure", 37 | "validate_relationship_integrity", 38 | ] 39 | -------------------------------------------------------------------------------- /src/tux/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Custom Modules 2 | 3 | This directory is for custom modules created by self-hosters. Any Python modules placed in this directory will be automatically discovered and loaded by the bot. 4 | 5 | ## Creating a Custom Module 6 | 7 | 1. Create a new Python file in this directory (e.g., `my_custom_module.py`) 8 | 2. Define your cog class that inherits from `BaseCog` 9 | 3. Implement your commands and functionality 10 | 4. The module will be automatically loaded when the bot starts 11 | 12 | ## Example 13 | 14 | ```python 15 | from discord.ext import commands 16 | from tux.core.base_cog import BaseCog 17 | from tux.core.bot import Tux 18 | 19 | class MyCustomModule(BaseCog): 20 | def __init__(self, bot: Tux) -> None: 21 | super().__init__(bot) 22 | 23 | @commands.command(name="hello") 24 | async def hello_command(self, ctx: commands.Context) -> None: 25 | """Say hello!""" 26 | await ctx.send("Hello from my custom module!") 27 | 28 | async def setup(bot: Tux) -> None: 29 | await bot.add_cog(MyCustomModule(bot)) 30 | ``` 31 | 32 | ## Notes 33 | 34 | - Custom modules have the same capabilities as built-in modules 35 | - They can use the dependency injection system 36 | - They follow the same patterns as core modules 37 | - Make sure to follow Python naming conventions for your module files 38 | -------------------------------------------------------------------------------- /compose.override.yaml.example: -------------------------------------------------------------------------------- 1 | --- 2 | # Development/Customization Override File 3 | # 4 | # Docker Compose automatically uses this file if it exists (it's gitignored). 5 | # Copy this to compose.override.yaml for local development or customizations. 6 | # 7 | # This allows developers and customizers to mount local files for faster iteration 8 | # without modifying the main compose.yaml file (which is production-ready). 9 | # 10 | # Usage: 11 | # cp compose.override.yaml.example compose.override.yaml 12 | # docker compose up -d 13 | # 14 | # When to use: 15 | # - Developing migrations (test without rebuilding image) 16 | # - Forking with custom models (iterate on custom migrations) 17 | # - Patching code with model changes (test patches quickly) 18 | # 19 | # When NOT to use: 20 | # - Production deployments (migrations come from image) 21 | # - Using official image without source code (mount will break) 22 | # - Using official image with outdated source (mount will break) 23 | 24 | services: 25 | tux: 26 | volumes: 27 | # Mount migrations for faster development/customization iteration 28 | # Without this, migrations come from the Docker image (production behavior) 29 | # Note: Mount to /app/src/tux/database/migrations (matches alembic.ini script_location) 30 | - ./src/tux/database/migrations:/app/src/tux/database/migrations:ro 31 | -------------------------------------------------------------------------------- /typings/typer/_typing.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import sys 6 | from typing import Any, Optional, Tuple, Type 7 | 8 | if sys.version_info >= (3, 9): 9 | ... 10 | else: 11 | ... 12 | if sys.version_info < (3, 10): 13 | ... 14 | else: 15 | def is_union(tp: Optional[Type[Any]]) -> bool: 16 | ... 17 | 18 | __all__ = ("NoneType", "is_none_type", "is_callable_type", "is_literal_type", "all_literal_values", "is_union", "Annotated", "Literal", "get_args", "get_origin", "get_type_hints") 19 | NoneType = None.__class__ 20 | NONE_TYPES: Tuple[Any, Any, Any] = ... 21 | if sys.version_info < (3, 8): 22 | ... 23 | else: 24 | def is_none_type(type_: Any) -> bool: 25 | ... 26 | 27 | def is_none_type(type_: Any) -> bool: 28 | ... 29 | 30 | def is_callable_type(type_: Type[Any]) -> bool: 31 | ... 32 | 33 | def is_literal_type(type_: Type[Any]) -> bool: 34 | ... 35 | 36 | def literal_values(type_: Type[Any]) -> Tuple[Any, ...]: 37 | ... 38 | 39 | def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]: 40 | """ 41 | This method is used to retrieve all Literal values as 42 | Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586) 43 | e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]` 44 | """ 45 | ... 46 | 47 | -------------------------------------------------------------------------------- /src/tux/database/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Database Models for Tux Bot. 3 | 4 | This module contains all SQLModel-based database models used by the Tux Discord bot, 5 | including base classes, mixins, enums, and specific model classes for various 6 | features like moderation, levels, snippets, and guild configuration. 7 | """ 8 | 9 | from __future__ import annotations 10 | 11 | # Import base classes and enums 12 | from .base import BaseModel, SoftDeleteMixin, UUIDMixin 13 | from .enums import CaseType, PermissionType 14 | from .models import ( 15 | AFK, 16 | Case, 17 | Guild, 18 | GuildConfig, 19 | Levels, 20 | PermissionAssignment, 21 | PermissionCommand, 22 | PermissionRank, 23 | Reminder, 24 | Snippet, 25 | Starboard, 26 | StarboardMessage, 27 | ) 28 | 29 | __all__ = [ 30 | # Base classes and mixins 31 | "BaseModel", 32 | "SoftDeleteMixin", 33 | "UUIDMixin", 34 | # Enums 35 | "CaseType", 36 | "PermissionType", 37 | # Core models 38 | "Guild", 39 | "GuildConfig", 40 | # User features 41 | "AFK", 42 | "Levels", 43 | "Reminder", 44 | "Snippet", 45 | # Moderation system 46 | "Case", 47 | # Permission system 48 | "PermissionRank", 49 | "PermissionAssignment", 50 | "PermissionCommand", 51 | # Starboard system 52 | "Starboard", 53 | "StarboardMessage", 54 | ] 55 | -------------------------------------------------------------------------------- /src/tux/database/models/enums.py: -------------------------------------------------------------------------------- 1 | """ 2 | Database model enums for Tux bot. 3 | 4 | This module defines enumeration types used throughout the database models, 5 | providing type-safe constants for permissions, onboarding stages, and case types. 6 | """ 7 | 8 | from __future__ import annotations 9 | 10 | from enum import Enum 11 | 12 | 13 | class PermissionType(str, Enum): 14 | """Types of permissions that can be configured in the system.""" 15 | 16 | MEMBER = "member" 17 | CHANNEL = "channel" 18 | CATEGORY = "category" 19 | ROLE = "role" 20 | COMMAND = "command" 21 | MODULE = "module" 22 | 23 | 24 | class OnboardingStage(str, Enum): 25 | """Stages of the guild onboarding process.""" 26 | 27 | NOT_STARTED = "not_started" 28 | DISCOVERED = "discovered" 29 | INITIALIZED = "initialized" 30 | CONFIGURED = "configured" 31 | COMPLETED = "completed" 32 | 33 | 34 | class CaseType(str, Enum): 35 | """Types of moderation cases that can be recorded in the system.""" 36 | 37 | BAN = "BAN" 38 | UNBAN = "UNBAN" 39 | HACKBAN = "HACKBAN" 40 | TEMPBAN = "TEMPBAN" 41 | KICK = "KICK" 42 | TIMEOUT = "TIMEOUT" 43 | UNTIMEOUT = "UNTIMEOUT" 44 | WARN = "WARN" 45 | JAIL = "JAIL" 46 | UNJAIL = "UNJAIL" 47 | SNIPPETBAN = "SNIPPETBAN" 48 | SNIPPETUNBAN = "SNIPPETUNBAN" 49 | POLLBAN = "POLLBAN" 50 | POLLUNBAN = "POLLUNBAN" 51 | -------------------------------------------------------------------------------- /typings/emojis/db/utils.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | def get_emoji_aliases(): # -> dict[Any, Any]: 6 | ''' 7 | Returns all Emojis as a dict (key = alias, value = unicode). 8 | 9 | :rtype: dict 10 | ''' 11 | ... 12 | 13 | def get_emoji_by_code(code): # -> None: 14 | ''' 15 | Returns Emoji by Unicode code. 16 | 17 | :param code: Emoji Unicode code. 18 | :rtype: emojis.db.Emoji 19 | ''' 20 | ... 21 | 22 | def get_emoji_by_alias(alias): # -> Emoji | None: 23 | ''' 24 | Returns Emoji by alias. 25 | 26 | :param alias: Emoji alias. 27 | :rtype: emojis.db.Emoji 28 | ''' 29 | ... 30 | 31 | def get_emojis_by_tag(tag): # -> filter[Emoji]: 32 | ''' 33 | Returns all Emojis from selected tag. 34 | 35 | :param tag: Tag name to filter (case-insensitive). 36 | :rtype: iter 37 | ''' 38 | ... 39 | 40 | def get_emojis_by_category(category): # -> filter[Any]: 41 | ''' 42 | Returns all Emojis from selected category. 43 | 44 | :param tag: Category name to filter (case-insensitive). 45 | :rtype: iter 46 | ''' 47 | ... 48 | 49 | def get_tags(): # -> set[Any]: 50 | ''' 51 | Returns all tags available. 52 | 53 | :rtype: set 54 | ''' 55 | ... 56 | 57 | def get_categories(): # -> set[Any]: 58 | ''' 59 | Returns all categories available. 60 | 61 | :rtype: set 62 | ''' 63 | ... 64 | 65 | -------------------------------------------------------------------------------- /typings/typer/utils.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from typing import Any, Callable, Dict, Type 6 | from .models import ParamMeta, ParameterInfo 7 | 8 | class AnnotatedParamWithDefaultValueError(Exception): 9 | argument_name: str 10 | param_type: Type[ParameterInfo] 11 | def __init__(self, argument_name: str, param_type: Type[ParameterInfo]) -> None: 12 | ... 13 | 14 | def __str__(self) -> str: 15 | ... 16 | 17 | 18 | 19 | class MixedAnnotatedAndDefaultStyleError(Exception): 20 | argument_name: str 21 | annotated_param_type: Type[ParameterInfo] 22 | default_param_type: Type[ParameterInfo] 23 | def __init__(self, argument_name: str, annotated_param_type: Type[ParameterInfo], default_param_type: Type[ParameterInfo]) -> None: 24 | ... 25 | 26 | def __str__(self) -> str: 27 | ... 28 | 29 | 30 | 31 | class MultipleTyperAnnotationsError(Exception): 32 | argument_name: str 33 | def __init__(self, argument_name: str) -> None: 34 | ... 35 | 36 | def __str__(self) -> str: 37 | ... 38 | 39 | 40 | 41 | class DefaultFactoryAndDefaultValueError(Exception): 42 | argument_name: str 43 | param_type: Type[ParameterInfo] 44 | def __init__(self, argument_name: str, param_type: Type[ParameterInfo]) -> None: 45 | ... 46 | 47 | def __str__(self) -> str: 48 | ... 49 | 50 | 51 | 52 | def get_params_from_function(func: Callable[..., Any]) -> Dict[str, ParamMeta]: 53 | ... 54 | 55 | -------------------------------------------------------------------------------- /src/tux/core/setup/permission_setup.py: -------------------------------------------------------------------------------- 1 | """Permission system setup service for bot initialization.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | from tux.core.permission_system import init_permission_system 8 | from tux.core.setup.base import BotSetupService 9 | from tux.database.controllers import DatabaseCoordinator 10 | 11 | if TYPE_CHECKING: 12 | from tux.core.bot import Tux 13 | from tux.database.service import DatabaseService 14 | 15 | __all__ = ["PermissionSetupService"] 16 | 17 | 18 | class PermissionSetupService(BotSetupService): 19 | """Handles permission system initialization during bot setup.""" 20 | 21 | def __init__(self, bot: Tux, db_service: DatabaseService) -> None: 22 | """Initialize the permission setup service. 23 | 24 | Parameters 25 | ---------- 26 | bot : Tux 27 | The Discord bot instance to set up permissions for. 28 | db_service : DatabaseService 29 | The database service instance for storing permissions. 30 | """ 31 | super().__init__(bot, "permissions") 32 | self.db_service = db_service 33 | 34 | async def setup(self) -> None: 35 | """Set up the permission system for command authorization.""" 36 | self._log_step("Initializing permission system...") 37 | 38 | db_coordinator = DatabaseCoordinator(self.db_service) 39 | init_permission_system(self.bot, db_coordinator) 40 | 41 | self._log_step("Permission system initialized successfully", "success") 42 | -------------------------------------------------------------------------------- /typings/reactionmenu/decorators.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | """ 6 | MIT License 7 | 8 | Copyright (c) 2021-present @defxult 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a 11 | copy of this software and associated documentation files (the "Software"), 12 | to deal in the Software without restriction, including without limitation 13 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | and/or sell copies of the Software, and to permit persons to whom the 15 | Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | """ 28 | 29 | def ensure_not_primed( 30 | func, 31 | ): # -> _Wrapped[Callable[..., Any], Any, Callable[..., Any], Coroutine[Any, Any, Any]] | _Wrapped[Callable[..., Any], Any, Callable[..., Any], Any]: 32 | """Check to make sure certain methods cannot be ran once the menu has been fully started""" 33 | -------------------------------------------------------------------------------- /src/tux/modules/moderation/report.py: -------------------------------------------------------------------------------- 1 | """ 2 | User reporting system for Discord servers. 3 | 4 | This module provides an anonymous reporting system that allows users to report 5 | issues, users, or content to server moderators through a modal interface. 6 | """ 7 | 8 | import discord 9 | from discord import app_commands 10 | 11 | from tux.core.base_cog import BaseCog 12 | from tux.core.bot import Tux 13 | from tux.ui.modals.report import ReportModal 14 | 15 | 16 | class Report(BaseCog): 17 | """Discord cog for user reporting functionality.""" 18 | 19 | def __init__(self, bot: Tux) -> None: 20 | """Initialize the Report cog. 21 | 22 | Parameters 23 | ---------- 24 | bot : Tux 25 | The bot instance to attach this cog to. 26 | """ 27 | super().__init__(bot) 28 | 29 | @app_commands.command(name="report") 30 | @app_commands.guild_only() 31 | async def report(self, interaction: discord.Interaction) -> None: 32 | """ 33 | Report a user or issue anonymously. 34 | 35 | Parameters 36 | ---------- 37 | interaction : discord.Interaction 38 | The interaction that triggered the command. 39 | """ 40 | modal = ReportModal(bot=self.bot) 41 | 42 | await interaction.response.send_modal(modal) 43 | 44 | 45 | async def setup(bot: Tux) -> None: 46 | """Set up the Report cog. 47 | 48 | Parameters 49 | ---------- 50 | bot : Tux 51 | The bot instance to add the cog to. 52 | """ 53 | await bot.add_cog(Report(bot)) 54 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Tux"; 3 | 4 | inputs = { 5 | nixpkgs = { 6 | type = "github"; 7 | owner = "NixOS"; 8 | repo = "nixpkgs"; 9 | ref = "nixos-unstable"; 10 | }; 11 | 12 | flake-parts = { 13 | type = "github"; 14 | owner = "hercules-ci"; 15 | repo = "flake-parts"; 16 | ref = "main"; 17 | }; 18 | }; 19 | 20 | outputs = inputs@{ 21 | self, 22 | nixpkgs, 23 | flake-parts, 24 | ... 25 | }: 26 | flake-parts.lib.mkFlake { inherit inputs; } { 27 | systems = [ 28 | "x86_64-linux" 29 | "x86_64-darwin" 30 | "aarch64-linux" 31 | "aarch64-darwin" 32 | ]; 33 | 34 | perSystem = { pkgs, self', system, ... }: { 35 | devShells = { 36 | default = self'.devShells.tux; 37 | tux = pkgs.callPackage ./shell.nix { inherit pkgs self; }; 38 | }; 39 | 40 | apps.envrc = { 41 | type = "app"; 42 | program = self'.packages.envrc; 43 | }; 44 | 45 | # Creates .envrc if does not exist 46 | packages.envrc = pkgs.writeShellScriptBin "envrc" '' 47 | echo 48 | 49 | if [ ! -e ".envrc" ]; then 50 | echo "Creating .envrc" 51 | printf "use flake .\n\n\n" | cat - .env.example > .envrc 52 | echo 53 | 54 | echo "The directory is now set up for direnv usage." 55 | echo 56 | else 57 | echo "Please delete .envrc if you wish to recreate it." 58 | echo 59 | fi 60 | ''; 61 | }; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /.vscode/python.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Cog": { 3 | "prefix": [ 4 | "cog", 5 | ], 6 | "scope": "python", 7 | "body": [ 8 | "\"\"\"${1:Module description}.", 9 | "", 10 | "${2:Longer description if needed.}", 11 | "\"\"\"", 12 | "", 13 | "from discord.ext import commands", 14 | "", 15 | "from tux.core.base_cog import BaseCog", 16 | "from tux.core.bot import Tux", 17 | "", 18 | "", 19 | "class ${3:CogName}(BaseCog):", 20 | " \"\"\"${4:Cog description}.\"\"\"", 21 | "", 22 | " def __init__(self, bot: Tux) -> None:", 23 | " \"\"\"Initialize the ${3:CogName} cog.", 24 | "", 25 | " Parameters", 26 | " ----------", 27 | " bot : Tux", 28 | " The bot instance to attach this cog to.", 29 | " \"\"\"", 30 | " super().__init__(bot)", 31 | "", 32 | " ${0:# Add commands here}", 33 | "", 34 | "", 35 | "async def setup(bot: Tux) -> None:", 36 | " \"\"\"Set up the ${3:CogName} cog.", 37 | "", 38 | " Parameters", 39 | " ----------", 40 | " bot : Tux", 41 | " The bot instance to add the cog to.", 42 | " \"\"\"", 43 | " await bot.add_cog(${3:CogName}(bot))" 44 | ], 45 | "description": "Discord.py cog using BaseCog" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.github/actions/create-test-env/action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Create Test Environment 3 | description: Create .env file with test configuration for CI/testing purposes using 4 | pydantic settings 5 | inputs: 6 | bot-token: 7 | description: Bot token for testing 8 | required: false 9 | default: test_token_for_ci 10 | additional-vars: 11 | description: Additional environment variables (KEY=value format, one per line) 12 | required: false 13 | default: '' 14 | runs: 15 | using: composite 16 | steps: 17 | # TEST ENVIRONMENT CONFIGURATION 18 | # Creates isolated test environment with safe defaults for pydantic settings 19 | - name: Create test environment file 20 | shell: bash 21 | run: |- 22 | # Create .env file for CI/testing with pydantic settings format 23 | cat > .env << EOF 24 | # Core configuration 25 | DEBUG=True 26 | 27 | # Bot token 28 | BOT_TOKEN=${{ inputs.bot-token }} 29 | 30 | # Database configuration (tests use py-pglite, so these are just defaults) 31 | POSTGRES_HOST=localhost 32 | POSTGRES_PORT=5432 33 | POSTGRES_DB=tuxdb_test 34 | POSTGRES_USER=tuxuser_test 35 | POSTGRES_PASSWORD=tuxpass_test 36 | 37 | # Bot info defaults 38 | BOT_INFO__BOT_NAME=Tux Test 39 | BOT_INFO__BOT_VERSION=0.0.0-test 40 | BOT_INFO__PREFIX=$ 41 | EOF 42 | 43 | # Add any additional environment variables if provided 44 | if [ -n "${{ inputs.additional-vars }}" ]; then 45 | echo "${{ inputs.additional-vars }}" >> .env 46 | fi 47 | echo "Test environment file created with pydantic settings format" 48 | -------------------------------------------------------------------------------- /alembic.ini: -------------------------------------------------------------------------------- 1 | [alembic] 2 | # path to migration scripts 3 | script_location = src/tux/database/migrations 4 | 5 | # template used to generate migration files 6 | file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s 7 | 8 | # sys.path path, will be prepended to sys.path 9 | prepend_sys_path = src 10 | 11 | # timezone to use when rendering the date within the migration file 12 | # as well as the filename. 13 | timezone = UTC 14 | 15 | # max length of characters to apply to the 16 | # "slug" field 17 | # truncate_slug_length = 40 18 | 19 | # set to 'true' to run the environment file as part of 20 | # the 'revision' environment script, instead of invoking 21 | # the migration class directly 22 | # revision_environment = false 23 | 24 | # set to 'true' to allow .pyc and .pyo files without 25 | # a source .py file to be detected as revisions in the 26 | # versions/ directory 27 | # sourceless = false 28 | 29 | # version path separator; defaults to os.sep 30 | # version_path_separator = os # Use 'os' if using os.sep 31 | 32 | # the output encoding used when revision files 33 | # are written from script.py.mako 34 | # output_encoding = utf-8 35 | 36 | # This setting is used by pytest-alembic to locate migration scripts 37 | version_locations = src/tux/database/migrations/versions 38 | 39 | # Database URL - will be overridden by env.py based on environment 40 | sqlalchemy.url = postgresql://placeholder 41 | 42 | [post_write_hooks] 43 | # Automatically format newly generated migration files using ruff 44 | hooks = ruff_format 45 | 46 | # Format using ruff 47 | ruff_format.type = exec 48 | ruff_format.executable = uv 49 | ruff_format.options = run ruff format REVISION_SCRIPT_FILENAME 50 | -------------------------------------------------------------------------------- /.github/scripts/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Docker workflow helper scripts 3 | # Usage: docker.sh [args...] 4 | 5 | set -euo pipefail 6 | 7 | # shellcheck disable=SC2034 8 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | 10 | # Generate PR version for Docker builds 11 | generate_pr_version() { 12 | local pr_number="${1}" 13 | local sha="${2}" 14 | local sha_prefix_length="${3:-7}" 15 | 16 | # Generate git describe format for PR builds to match VERSIONING.md expectations 17 | local pr_version 18 | pr_version="pr-${pr_number}-$(echo "$sha" | cut -c1-"${sha_prefix_length}")" 19 | echo "version=$pr_version" >> "$GITHUB_OUTPUT" 20 | echo "Generated PR version: $pr_version" 21 | } 22 | 23 | # Generate release version for Docker builds 24 | generate_release_version() { 25 | local github_ref="${1}" 26 | local sha_prefix_length="${2}" 27 | 28 | # Generate git describe format for release builds to match VERSIONING.md expectations 29 | # This ensures the VERSION file contains the exact format expected by __init__.py 30 | local tag_version="${github_ref#refs/tags/}" 31 | local clean_version="${tag_version#v}" # Remove 'v' prefix if present 32 | local release_version="$clean_version" 33 | echo "version=$release_version" >> "$GITHUB_OUTPUT" 34 | echo "Generated release version: $release_version" 35 | } 36 | 37 | COMMAND="${1:-}" 38 | shift || true 39 | 40 | case "$COMMAND" in 41 | generate-pr-version) 42 | generate_pr_version "$@" 43 | ;; 44 | generate-release-version) 45 | generate_release_version "$@" 46 | ;; 47 | *) 48 | echo "Usage: docker.sh {generate-pr-version|generate-release-version} [args...]" 49 | exit 1 50 | ;; 51 | esac 52 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request 2 | 3 | ## Description 4 | 5 | Provide a clear summary of your changes and reference any related issues. Include the motivation behind these changes and list any new dependencies if applicable. 6 | 7 | If your PR is related to an issue, please include the issue number below: 8 | 9 | **Related Issue:** Closes # 10 | 11 | **Type of Change:** 12 | 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 16 | - [ ] Documentation update 17 | - [ ] Performance improvement 18 | - [ ] Code refactoring 19 | - [ ] Test improvements 20 | 21 | ## Guidelines 22 | 23 | - My code follows the style guidelines of this project (formatted with Ruff) 24 | - I have performed a self-review of my own code 25 | - I have commented my code, particularly in hard-to-understand areas 26 | - I have made corresponding changes to the documentation if needed 27 | - My changes generate no new warnings 28 | - I have tested this change 29 | - Any dependent changes have been merged and published in downstream modules 30 | - I have added all appropriate labels to this PR 31 | 32 | - [ ] I have followed all of these guidelines. 33 | 34 | ## How Has This Been Tested? (if applicable) 35 | 36 | Please describe how you tested your code. e.g describe what commands you ran, what arguments, and any config stuff (if applicable) 37 | 38 | ## Screenshots (if applicable) 39 | 40 | Please add screenshots to help explain your changes. 41 | 42 | ## Additional Information 43 | 44 | Please add any other information that is important to this PR. 45 | -------------------------------------------------------------------------------- /docs/content/getting-started/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | tags: 4 | - getting-started 5 | --- 6 | 7 | # Getting Started 8 | 9 | !!! warning "Work in progress" 10 | This section is a work in progress. Please help us by contributing to the documentation. 11 | 12 | Welcome to Tux! This guide will help you get started based on what you want to do. 13 | 14 | ## Navigation 15 | 16 | ### 👥 I Want to Use Tux 17 | 18 | You're a Discord server admin or user who wants to use Tux's features. 19 | 20 | **[Go to User Guide →](for-users.md)** 21 | 22 | You'll learn: 23 | 24 | - How to invite Tux to your server 25 | - Available commands and how to use them 26 | - Permission ranks and how they work 27 | - Features like XP system, starboard, and snippets 28 | 29 | ### ⚙️ I Want to Run My Own Tux 30 | 31 | You want to self-host Tux for your community. 32 | 33 | **[Go to Self-Hoster Guide →](for-self-hosters.md)** 34 | 35 | You'll learn: 36 | 37 | - How to deploy Tux (Docker, VPS, cloud platforms) 38 | - Database setup and migrations 39 | - Configuration options 40 | - Operations and maintenance 41 | 42 | ### 💡 I Want to Contribute to Tux 43 | 44 | You're a developer who wants to contribute code or build features. 45 | 46 | **[Go to Developer Guide →](for-developers.md)** 47 | 48 | You'll learn: 49 | 50 | - Development environment setup 51 | - Codebase architecture 52 | - Testing and CI/CD 53 | - Contributing guidelines 54 | 55 | ## Need Help? 56 | 57 | - **[Discord Support](https://discord.gg/gpmSjcjQxg)** - Join our community for help 58 | - **[GitHub Issues](https://github.com/allthingslinux/tux/issues)** - Report bugs or request features 59 | - **[FAQ](../faq/index.md)** - Common questions and answers 60 | 61 | Choose your path above to continue! 62 | -------------------------------------------------------------------------------- /typings/emojis/emojis.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | ALIAS_TO_EMOJI = ... 6 | EMOJI_TO_ALIAS = ... 7 | EMOJI_TO_ALIAS_SORTED = ... 8 | RE_TEXT_TO_EMOJI_GROUP = ... 9 | RE_TEXT_TO_EMOJI = ... 10 | RE_EMOJI_TO_TEXT_GROUP = ... 11 | RE_EMOJI_TO_TEXT = ... 12 | def encode(msg): # -> str: 13 | ''' 14 | Encode Emoji aliases into unicode Emoji values. 15 | 16 | :param msg: String to encode. 17 | :rtype: str 18 | 19 | Usage:: 20 | 21 | >>> import emojis 22 | >>> emojis.encode('This is a message with emojis :smile: :snake:') 23 | 'This is a message with emojis 😄 🐍' 24 | ''' 25 | ... 26 | 27 | def decode(msg): # -> str: 28 | ''' 29 | Decode unicode Emoji values into Emoji aliases. 30 | 31 | :param msg: String to decode. 32 | :rtype: str 33 | 34 | Usage:: 35 | 36 | >>> import emojis 37 | >>> emojis.decode('This is a message with emojis 😄 🐍') 38 | 'This is a message with emojis :smile: :snake:' 39 | ''' 40 | ... 41 | 42 | def get(msg): # -> set[str]: 43 | ''' 44 | Returns unique Emojis in the given string. 45 | 46 | :param msg: String to search for Emojis. 47 | :rtype: set 48 | ''' 49 | ... 50 | 51 | def iter(msg): # -> Generator[str, None, None]: 52 | ''' 53 | Iterates over all Emojis found in the message. 54 | 55 | :param msg: String to search for Emojis. 56 | :rtype: iterator 57 | ''' 58 | ... 59 | 60 | def count(msg, unique=...): # -> int: 61 | ''' 62 | Returns Emoji count in the given string. 63 | 64 | :param msg: String to search for Emojis. 65 | :param unique: (optional) Boolean, return unique values only. 66 | :rtype: int 67 | ''' 68 | ... 69 | 70 | -------------------------------------------------------------------------------- /config/config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "debug": false, 3 | "log_level": "INFO", 4 | "bot_token": "", 5 | "postgres_host": "localhost", 6 | "postgres_port": 5432, 7 | "postgres_db": "tuxdb", 8 | "postgres_user": "tuxuser", 9 | "postgres_password": "ChangeThisToAStrongPassword123!", 10 | "database_url": "", 11 | "allow_sysadmins_eval": false, 12 | "bot_info": { 13 | "bot_name": "Tux", 14 | "activities": [], 15 | "hide_bot_owner": false, 16 | "prefix": "$" 17 | }, 18 | "user_ids": { 19 | "bot_owner_id": 0, 20 | "sysadmins": [] 21 | }, 22 | "status_roles": { 23 | "mappings": [] 24 | }, 25 | "temp_vc": { 26 | "tempvc_channel_id": "null", 27 | "tempvc_category_id": "null" 28 | }, 29 | "gif_limiter": { 30 | "recent_gif_age": 60, 31 | "gif_limits_user": {}, 32 | "gif_limits_channel": {}, 33 | "gif_limit_exclude": [] 34 | }, 35 | "xp": { 36 | "xp_blacklist_channels": [], 37 | "xp_roles": [], 38 | "xp_multipliers": [], 39 | "xp_cooldown": 1, 40 | "levels_exponent": 2, 41 | "show_xp_progress": true, 42 | "enable_xp_cap": false 43 | }, 44 | "snippets": { 45 | "limit_to_role_ids": false, 46 | "access_role_ids": [] 47 | }, 48 | "irc": { 49 | "bridge_webhook_ids": [] 50 | }, 51 | "external_services": { 52 | "sentry_dsn": "", 53 | "github_app_id": "", 54 | "github_installation_id": "", 55 | "github_private_key": "", 56 | "github_client_id": "", 57 | "github_client_secret": "", 58 | "github_repo_url": "", 59 | "github_repo_owner": "", 60 | "github_repo": "", 61 | "mailcow_api_key": "", 62 | "mailcow_api_url": "", 63 | "wolfram_app_id": "", 64 | "influxdb_token": "", 65 | "influxdb_url": "", 66 | "influxdb_org": "" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-parts": { 4 | "inputs": { 5 | "nixpkgs-lib": "nixpkgs-lib" 6 | }, 7 | "locked": { 8 | "lastModified": 1763759067, 9 | "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=", 10 | "owner": "hercules-ci", 11 | "repo": "flake-parts", 12 | "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "hercules-ci", 17 | "ref": "main", 18 | "repo": "flake-parts", 19 | "type": "github" 20 | } 21 | }, 22 | "nixpkgs": { 23 | "locked": { 24 | "lastModified": 1764950072, 25 | "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", 26 | "owner": "NixOS", 27 | "repo": "nixpkgs", 28 | "rev": "f61125a668a320878494449750330ca58b78c557", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "NixOS", 33 | "ref": "nixos-unstable", 34 | "repo": "nixpkgs", 35 | "type": "github" 36 | } 37 | }, 38 | "nixpkgs-lib": { 39 | "locked": { 40 | "lastModified": 1761765539, 41 | "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=", 42 | "owner": "nix-community", 43 | "repo": "nixpkgs.lib", 44 | "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc", 45 | "type": "github" 46 | }, 47 | "original": { 48 | "owner": "nix-community", 49 | "repo": "nixpkgs.lib", 50 | "type": "github" 51 | } 52 | }, 53 | "root": { 54 | "inputs": { 55 | "flake-parts": "flake-parts", 56 | "nixpkgs": "nixpkgs" 57 | } 58 | } 59 | }, 60 | "root": "root", 61 | "version": 7 62 | } 63 | -------------------------------------------------------------------------------- /src/tux/ui/buttons.py: -------------------------------------------------------------------------------- 1 | """ 2 | Discord UI Button Components for Tux Bot. 3 | 4 | This module provides reusable Discord UI button components for various bot features, 5 | including xkcd comic links and GitHub repository links. 6 | """ 7 | 8 | import discord 9 | 10 | 11 | class XkcdButtons(discord.ui.View): 12 | """Button view for xkcd comic links.""" 13 | 14 | def __init__(self, explain_url: str, webpage_url: str) -> None: 15 | """Initialize xkcd buttons with explain and webpage links. 16 | 17 | Parameters 18 | ---------- 19 | explain_url : str 20 | URL to the explainxkcd page for the comic. 21 | webpage_url : str 22 | URL to the original xkcd webpage. 23 | """ 24 | super().__init__() 25 | self.add_item( 26 | discord.ui.Button( 27 | style=discord.ButtonStyle.link, 28 | label="Explainxkcd", 29 | url=explain_url, 30 | ), 31 | ) 32 | self.add_item( 33 | discord.ui.Button( 34 | style=discord.ButtonStyle.link, 35 | label="Webpage", 36 | url=webpage_url, 37 | ), 38 | ) 39 | 40 | 41 | class GithubButton(discord.ui.View): 42 | """Button view for GitHub repository links.""" 43 | 44 | def __init__(self, url: str) -> None: 45 | """Initialize GitHub button with repository URL. 46 | 47 | Parameters 48 | ---------- 49 | url : str 50 | URL to the GitHub repository or issue. 51 | """ 52 | super().__init__() 53 | self.add_item( 54 | discord.ui.Button( 55 | style=discord.ButtonStyle.link, 56 | label="View on Github", 57 | url=url, 58 | ), 59 | ) 60 | -------------------------------------------------------------------------------- /src/tux/database/controllers/base/filters.py: -------------------------------------------------------------------------------- 1 | """Shared filter utilities for database controllers.""" 2 | 3 | from typing import Any 4 | 5 | from sqlalchemy import BinaryExpression, and_ 6 | 7 | 8 | def build_filters_for_model( 9 | filters: dict[str, Any] | Any, 10 | model: type[Any], 11 | ) -> BinaryExpression[bool] | Any | None: 12 | """ 13 | Build filter expressions from various input types for a specific model. 14 | 15 | Returns 16 | ------- 17 | BinaryExpression[bool] | Any | None 18 | Combined filter expression, or None if no filters. 19 | """ 20 | if filters is None: 21 | return None 22 | 23 | if isinstance(filters, dict): 24 | filter_expressions: list[BinaryExpression[bool]] = [ 25 | getattr(model, key) == value # type: ignore[arg-type] 26 | for key, value in filters.items() # type: ignore[var-annotated] 27 | ] 28 | return and_(*filter_expressions) if filter_expressions else None 29 | 30 | # Handle iterable of SQL expressions (but not strings/bytes) 31 | if hasattr(filters, "__iter__") and not isinstance(filters, str | bytes): 32 | return and_(*filters) 33 | 34 | # Return single filter expression as-is 35 | return filters 36 | 37 | 38 | def build_filters(filters: Any) -> Any: 39 | """ 40 | Build filter expressions from various input types (legacy function). 41 | 42 | Returns 43 | ------- 44 | Any 45 | Combined filter expression, or None if no filters. 46 | """ 47 | if filters is None: 48 | return None 49 | 50 | # Handle iterable of SQL expressions (but not strings/bytes) 51 | if hasattr(filters, "__iter__") and not isinstance(filters, str | bytes): 52 | return and_(*filters) 53 | 54 | # Return single filter expression as-is 55 | return filters 56 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/generators/dotenv.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import sys 6 | from pathlib import Path 7 | from typing import Literal, Optional, Self 8 | from pydantic import model_validator 9 | from pydantic_settings_export.models import SettingsInfoModel 10 | from .abstract import AbstractGenerator, BaseGeneratorSettings 11 | 12 | if sys.version_info < (3, 11): 13 | ... 14 | else: 15 | ... 16 | __all__ = ("DotEnvGenerator", ) 17 | DotEnvMode = Literal["all", "only-optional", "only-required"] 18 | DOTENV_MODE_MAP: dict[DotEnvMode, tuple[bool, bool]] = ... 19 | DOTENV_MODE_MAP_DEFAULT = ... 20 | class DotEnvSettings(BaseGeneratorSettings): 21 | """Settings for the .env file.""" 22 | model_config = ... 23 | name: Optional[Path] = ... 24 | paths: list[Path] = ... 25 | split_by_group: bool = ... 26 | add_examples: bool = ... 27 | mode: DotEnvMode = ... 28 | @model_validator(mode="after") 29 | def validate_paths(self) -> Self: 30 | """Validate the paths.""" 31 | ... 32 | 33 | 34 | 35 | class DotEnvGenerator(AbstractGenerator[DotEnvSettings]): 36 | """The .env example generator.""" 37 | name = ... 38 | config = DotEnvSettings 39 | def generate_single(self, settings_info: SettingsInfoModel, level: int = ...) -> str: 40 | """Generate a .env example for a pydantic settings class. 41 | 42 | Creates a formatted .env file with: 43 | - Optional/required variables clearly marked 44 | - Grouped settings with headers 45 | - Example values as comments 46 | - Proper environment variable naming 47 | 48 | :param settings_info: The settings class to generate examples for. 49 | :param level: The level of nesting for proper formatting. 50 | :return: Formatted .env content with variables and documentation. 51 | """ 52 | ... 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /typings/py_pglite/sqlalchemy/fixtures.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import pytest 6 | from collections.abc import Generator 7 | from typing import Any 8 | from sqlalchemy.engine import Engine 9 | from sqlalchemy.orm import Session 10 | from ..config import PGliteConfig 11 | from .manager import SQLAlchemyPGliteManager 12 | 13 | """SQLAlchemy-specific pytest fixtures for PGlite integration.""" 14 | HAS_SQLMODEL = ... 15 | logger = ... 16 | @pytest.fixture(scope="session") 17 | def pglite_config() -> PGliteConfig: 18 | """Pytest fixture providing PGlite configuration.""" 19 | ... 20 | 21 | @pytest.fixture(scope="session") 22 | def pglite_sqlalchemy_manager(pglite_config: PGliteConfig) -> Generator[SQLAlchemyPGliteManager, None, None]: 23 | """Pytest fixture providing an SQLAlchemy-enabled PGlite manager.""" 24 | ... 25 | 26 | @pytest.fixture(scope="session") 27 | def pglite_engine(pglite_sqlalchemy_manager: SQLAlchemyPGliteManager) -> Engine: 28 | """Pytest fixture providing a SQLAlchemy engine connected to PGlite. 29 | 30 | Uses the SQLAlchemy-enabled manager to ensure proper SQLAlchemy integration. 31 | """ 32 | ... 33 | 34 | @pytest.fixture(scope="session") 35 | def pglite_sqlalchemy_engine(pglite_sqlalchemy_manager: SQLAlchemyPGliteManager) -> Engine: 36 | """Pytest fixture providing an optimized SQLAlchemy engine connected to PGlite.""" 37 | ... 38 | 39 | @pytest.fixture(scope="function") 40 | def pglite_session(pglite_engine: Engine) -> Generator[Any, None, None]: 41 | """Pytest fixture providing a SQLAlchemy/SQLModel session with proper isolation. 42 | 43 | This fixture ensures database isolation between tests by cleaning all data 44 | at the start of each test. 45 | """ 46 | ... 47 | 48 | @pytest.fixture(scope="function") 49 | def pglite_sqlalchemy_session(pglite_session: Session) -> Session: 50 | """Legacy fixture name for backwards compatibility.""" 51 | ... 52 | 53 | -------------------------------------------------------------------------------- /docs/content/selfhost/install/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Installation 3 | tags: 4 | - selfhost 5 | - installation 6 | --- 7 | 8 | # Installation 9 | 10 | Review the below information and pick the installation method that best suits your environment. 11 | 12 | ## 1. Review Requirements 13 | 14 | These are the system requirements needed to install and run Tux. 15 | 16 | ### Minimum Requirements 17 | 18 | - **OS**: Any 19 | - **RAM**: 512MB 20 | - **Storage**: 2GB free space 21 | 22 | ### Recommended Requirements 23 | 24 | - **OS**: Linux 25 | - **RAM**: 2GB+ 26 | - **Storage**: 5GB+ free space 27 | 28 | ### Software Dependencies 29 | 30 | #### Required 31 | 32 | - **Python**: 3.13+ 33 | - **uv**: Project management tool 34 | - **Git**: For cloning the repository 35 | - **Docker or Podman**: For containerized deployment (optional) 36 | 37 | !!! note "Note" 38 | If you use Docker or Podman, also install Docker Compose V2 or Podman Compose. 39 | 40 | #### Database 41 | 42 | - **PostgreSQL**: 17+ (required) 43 | 44 | !!! tip "Tip" 45 | If you don't want to manage the database yourself, consider using a managed PostgreSQL service such as [Supabase](https://supabase.com/). 46 | 47 | ### Discord Bot Requirements 48 | 49 | - **Bot Token**: Read [our guide](../config/bot-token.md) on obtaining a bot token. 50 | - **Permissions**: Administrator or specific permissions as needed 51 | - **Server Access**: Bot must be invited to target servers 52 | 53 | ## 2. Database Setup 54 | 55 | Refer to the [Database Installation](database.md) guide to setup the PostgreSQL database before proceeding with the installation. 56 | 57 | ## 3. Choose Installation Method 58 | 59 | Choose one of the following installation methods: 60 | 61 | - [Docker Installation](docker.md) - Recommended for most users 62 | - [Systemd Installation](systemd.md) - For non-Docker setups 63 | 64 | ## 4. First Run 65 | 66 | After installation, follow the [First Run Setup](first-run.md) guide to complete the initial configuration and get Tux running. 67 | -------------------------------------------------------------------------------- /typings/typer/_completion_classes.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import click 6 | import click.shell_completion 7 | from typing import Any, Dict, List, Tuple 8 | 9 | class BashComplete(click.shell_completion.BashComplete): 10 | name = ... 11 | source_template = ... 12 | def source_vars(self) -> Dict[str, Any]: 13 | ... 14 | 15 | def get_completion_args(self) -> Tuple[List[str], str]: 16 | ... 17 | 18 | def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 19 | ... 20 | 21 | def complete(self) -> str: 22 | ... 23 | 24 | 25 | 26 | class ZshComplete(click.shell_completion.ZshComplete): 27 | name = ... 28 | source_template = ... 29 | def source_vars(self) -> Dict[str, Any]: 30 | ... 31 | 32 | def get_completion_args(self) -> Tuple[List[str], str]: 33 | ... 34 | 35 | def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 36 | ... 37 | 38 | def complete(self) -> str: 39 | ... 40 | 41 | 42 | 43 | class FishComplete(click.shell_completion.FishComplete): 44 | name = ... 45 | source_template = ... 46 | def source_vars(self) -> Dict[str, Any]: 47 | ... 48 | 49 | def get_completion_args(self) -> Tuple[List[str], str]: 50 | ... 51 | 52 | def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 53 | ... 54 | 55 | def complete(self) -> str: 56 | ... 57 | 58 | 59 | 60 | class PowerShellComplete(click.shell_completion.ShellComplete): 61 | name = ... 62 | source_template = ... 63 | def source_vars(self) -> Dict[str, Any]: 64 | ... 65 | 66 | def get_completion_args(self) -> Tuple[List[str], str]: 67 | ... 68 | 69 | def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 70 | ... 71 | 72 | 73 | 74 | def completion_init() -> None: 75 | ... 76 | 77 | -------------------------------------------------------------------------------- /typings/py_pglite/config.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from dataclasses import dataclass 6 | from pathlib import Path 7 | 8 | """Configuration for PGlite testing.""" 9 | @dataclass 10 | class PGliteConfig: 11 | """Configuration for PGlite test database. 12 | 13 | Args: 14 | timeout: Timeout in seconds for PGlite startup (default: 30) 15 | cleanup_on_exit: Whether to cleanup socket/process on exit (default: True) 16 | log_level: Logging level for PGlite operations (default: "INFO") 17 | socket_path: Custom socket path (default: secure temp directory) 18 | work_dir: Working directory for PGlite files (default: None, uses temp) 19 | node_modules_check: Whether to verify node_modules exists (default: True) 20 | auto_install_deps: Whether to auto-install npm dependencies (default: True) 21 | extensions: List of PGlite extensions to enable (e.g., ["pgvector"]) 22 | node_options: Custom NODE_OPTIONS for the Node.js process 23 | """ 24 | timeout: int = ... 25 | cleanup_on_exit: bool = ... 26 | log_level: str = ... 27 | socket_path: str = ... 28 | work_dir: Path | None = ... 29 | node_modules_check: bool = ... 30 | auto_install_deps: bool = ... 31 | extensions: list[str] | None = ... 32 | node_options: str | None = ... 33 | def __post_init__(self) -> None: 34 | """Validate configuration after initialization.""" 35 | ... 36 | 37 | @property 38 | def log_level_int(self) -> int: 39 | """Get logging level as integer.""" 40 | ... 41 | 42 | def get_connection_string(self) -> str: 43 | """Get PostgreSQL connection string for SQLAlchemy usage.""" 44 | ... 45 | 46 | def get_psycopg_uri(self) -> str: 47 | """Get PostgreSQL URI for direct psycopg usage.""" 48 | ... 49 | 50 | def get_dsn(self) -> str: 51 | """Get PostgreSQL DSN connection string for direct psycopg usage.""" 52 | ... 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/tux/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tux Discord Bot Main Entry Point. 3 | 4 | This module serves as the main entry point for the Tux Discord bot application. 5 | It handles application initialization, error handling, and provides the run() 6 | function that starts the bot with proper lifecycle management. 7 | """ 8 | 9 | import sys 10 | 11 | from loguru import logger 12 | 13 | from tux.core.app import TuxApp 14 | from tux.shared.exceptions import TuxDatabaseError, TuxError 15 | 16 | 17 | def run() -> int: 18 | """ 19 | Instantiate and run the Tux application. 20 | 21 | This function is the entry point for the Tux application. 22 | It creates an instance of the TuxApp class. 23 | 24 | Returns 25 | ------- 26 | int 27 | Exit code: 0 for success, non-zero for failure 28 | 29 | Notes 30 | ----- 31 | Logging is configured by the CLI script (scripts/base.py) before this is called. 32 | """ 33 | try: 34 | logger.info("Starting Tux...") 35 | app = TuxApp() 36 | return app.run() 37 | 38 | except (TuxDatabaseError, TuxError, SystemExit, KeyboardInterrupt, Exception) as e: 39 | # Handle all errors in one place 40 | if isinstance(e, TuxDatabaseError): 41 | logger.error("Database connection failed") 42 | logger.info("To start the database, run: make docker-up") 43 | elif isinstance(e, TuxError): 44 | logger.error(f"Bot startup failed: {e}") 45 | elif isinstance(e, RuntimeError): 46 | logger.critical(f"Application failed to start: {e}") 47 | elif isinstance(e, SystemExit): 48 | return int(e.code) if e.code is not None else 1 49 | elif isinstance(e, KeyboardInterrupt): 50 | logger.info("Shutdown requested by user") 51 | return 0 52 | else: 53 | logger.opt(exception=True).critical(f"Application failed to start: {e}") 54 | 55 | return 1 56 | 57 | else: 58 | return 0 59 | 60 | 61 | if __name__ == "__main__": 62 | exit_code = run() 63 | sys.exit(exit_code) 64 | -------------------------------------------------------------------------------- /src/tux/core/setup/cog_setup.py: -------------------------------------------------------------------------------- 1 | """Cog setup service for bot initialization.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | from discord.ext import commands 8 | 9 | from tux.core.cog_loader import CogLoader 10 | from tux.core.setup.base import BotSetupService 11 | 12 | if TYPE_CHECKING: 13 | from tux.core.bot import Tux 14 | 15 | __all__ = ["CogSetupService"] 16 | 17 | 18 | class CogSetupService(BotSetupService): 19 | """Handles cog loading and plugin setup during bot initialization.""" 20 | 21 | def __init__(self, bot: Tux) -> None: 22 | """Initialize the cog setup service. 23 | 24 | Parameters 25 | ---------- 26 | bot : Tux 27 | The Discord bot instance to load cogs for. 28 | """ 29 | super().__init__(bot, "cogs") 30 | 31 | async def setup(self) -> None: 32 | """Load all cogs and plugins.""" 33 | await self._load_jishaku() 34 | await self._load_cogs() 35 | await self._load_hot_reload() 36 | 37 | async def _load_jishaku(self) -> None: 38 | """Load Jishaku development plugin.""" 39 | try: 40 | await self.bot.load_extension("jishaku") 41 | self._log_step("Jishaku plugin loaded", "success") 42 | except commands.ExtensionError as e: 43 | self._log_step(f"Jishaku plugin not loaded: {e}", "warning") 44 | 45 | async def _load_cogs(self) -> None: 46 | """Load all bot cogs using CogLoader.""" 47 | self._log_step("Loading cogs...") 48 | await CogLoader.setup(self.bot) 49 | self._log_step("All cogs loaded", "success") 50 | 51 | async def _load_hot_reload(self) -> None: 52 | """Load hot reload system.""" 53 | if "tux.services.hot_reload" not in self.bot.extensions: 54 | try: 55 | await self.bot.load_extension("tux.services.hot_reload") 56 | self._log_step("Hot reload system initialized", "success") 57 | except Exception as e: 58 | self._log_step(f"Hot reload failed to load: {e}", "warning") 59 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Default settings for all files 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | # Python files 16 | [*.py] 17 | indent_size = 4 18 | max_line_length = 88 19 | 20 | # Python stub files 21 | [*.pyi] 22 | indent_size = 4 23 | max_line_length = 88 24 | 25 | # Configuration files (YAML, TOML, JSON) 26 | [*.{yaml,yml}] 27 | indent_size = 2 28 | 29 | [*.toml] 30 | indent_size = 4 31 | 32 | [*.json] 33 | indent_size = 2 34 | 35 | # Docker files 36 | [Containerfile] 37 | indent_size = 8 38 | 39 | [compose.yaml] 40 | indent_size = 2 41 | 42 | # Shell scripts 43 | # shfmt configuration matches pre-commit hook: -ln bash -i 2 -ci -bn -sr -kp -s 44 | [*.{sh,bash,zsh,fish}] 45 | indent_style = space 46 | indent_size = 2 47 | # shfmt-specific properties (shfmt 3.8.0+) 48 | shell_variant = bash 49 | binary_next_line = true 50 | switch_case_indent = true 51 | space_redirects = true 52 | keep_padding = true 53 | 54 | # Nix files 55 | [*.nix] 56 | indent_size = 2 57 | 58 | # Web files (if any) 59 | [*.{html,css,js,ts,jsx,tsx}] 60 | indent_size = 2 61 | 62 | # Markdown files 63 | [*.md] 64 | indent_size = 2 65 | trim_trailing_whitespace = false 66 | 67 | # Environment files 68 | [.env*] 69 | indent_size = 4 70 | 71 | # Git files 72 | [.git*] 73 | indent_size = 4 74 | 75 | # Lock files (read-only, preserve formatting) 76 | [{uv.lock,package-lock.json,yarn.lock,Pipfile.lock}] 77 | insert_final_newline = false 78 | trim_trailing_whitespace = false 79 | 80 | # Makefile (requires tabs) 81 | [{Makefile,makefile,*.mk}] 82 | indent_style = tab 83 | indent_size = 4 84 | 85 | # Batch files (Windows) 86 | [*.{bat,cmd}] 87 | end_of_line = crlf 88 | 89 | # Archive directory (preserve original formatting) 90 | [.archive/**] 91 | insert_final_newline = false 92 | trim_trailing_whitespace = false 93 | 94 | # Generated/cache directories (ignore) 95 | [{__pycache__,*.pyc,.mypy_cache,.pytest_cache,.ruff_cache,node_modules}/**] 96 | insert_final_newline = false 97 | trim_trailing_whitespace = false 98 | -------------------------------------------------------------------------------- /typings/typer/cli.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import click 6 | import typer 7 | import typer.core 8 | from pathlib import Path 9 | from typing import Any, List, Optional 10 | from click import Command, Group, Option 11 | 12 | has_rich = ... 13 | default_app_names = ... 14 | default_func_names = ... 15 | app = ... 16 | utils_app = ... 17 | class State: 18 | def __init__(self) -> None: 19 | ... 20 | 21 | 22 | 23 | state = ... 24 | def maybe_update_state(ctx: click.Context) -> None: 25 | ... 26 | 27 | class TyperCLIGroup(typer.core.TyperGroup): 28 | def list_commands(self, ctx: click.Context) -> List[str]: 29 | ... 30 | 31 | def get_command(self, ctx: click.Context, name: str) -> Optional[Command]: 32 | ... 33 | 34 | def invoke(self, ctx: click.Context) -> Any: 35 | ... 36 | 37 | def maybe_add_run(self, ctx: click.Context) -> None: 38 | ... 39 | 40 | 41 | 42 | def get_typer_from_module(module: Any) -> Optional[typer.Typer]: 43 | ... 44 | 45 | def get_typer_from_state() -> Optional[typer.Typer]: 46 | ... 47 | 48 | def maybe_add_run_to_cli(cli: click.Group) -> None: 49 | ... 50 | 51 | def print_version(ctx: click.Context, param: Option, value: bool) -> None: 52 | ... 53 | 54 | @app.callback(cls=TyperCLIGroup, no_args_is_help=True) 55 | def callback(ctx: typer.Context, *, path_or_module: str = ..., app: str = ..., func: str = ..., version: bool = ...) -> None: 56 | """ 57 | Run Typer scripts with completion, without having to create a package. 58 | 59 | You probably want to install completion for the typer command: 60 | 61 | $ typer --install-completion 62 | 63 | https://typer.tiangolo.com/ 64 | """ 65 | ... 66 | 67 | def get_docs_for_click(*, obj: Command, ctx: typer.Context, indent: int = ..., name: str = ..., call_prefix: str = ..., title: Optional[str] = ...) -> str: 68 | ... 69 | 70 | @utils_app.command() 71 | def docs(ctx: typer.Context, name: str = ..., output: Optional[Path] = ..., title: Optional[str] = ...) -> None: 72 | """ 73 | Generate Markdown docs for a Typer app. 74 | """ 75 | ... 76 | 77 | def main() -> Any: 78 | ... 79 | 80 | -------------------------------------------------------------------------------- /.github/actions/action-basedpyright/action.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: action-basedpyright 3 | description: Run basedpyright with reviewdog on pull requests to improve code review 4 | experience 5 | inputs: 6 | github_token: 7 | description: GITHUB_TOKEN 8 | default: ${{ github.token }} 9 | workdir: 10 | description: Working directory relative to the root directory. 11 | default: . 12 | ### Flags for reviewdog ### 13 | tool_name: 14 | description: Tool name to use for reviewdog reporter. 15 | default: basedpyright 16 | level: 17 | description: Report level for reviewdog [info,warning,error]. 18 | default: warning 19 | reporter: 20 | description: Reporter of reviewdog command [github-check,github-pr-review,github-pr-check,sarif]. 21 | default: github-pr-review 22 | filter_mode: 23 | description: | 24 | Filtering mode for the reviewdog command [added,diff_context,file,nofilter]. 25 | Default is `added` except that sarif reporter uses `nofilter`. 26 | default: file 27 | fail_level: 28 | description: | 29 | If set to `none`, always use exit code 0 for reviewdog. Otherwise, exit code 1 for reviewdog if it finds at least 1 issue with severity greater than or equal to the given level. 30 | Possible values: [none,any,info,warning,error] 31 | Default is `none`. 32 | default: none 33 | reviewdog_flags: 34 | description: Additional reviewdog flags. 35 | default: '' 36 | ### Flags for basedpyright ### 37 | basedpyright_flags: 38 | description: Additional flags for basedpyright command. 39 | default: --outputjson 40 | runs: 41 | using: composite 42 | steps: 43 | - name: Run basedpyright with reviewdog 44 | shell: bash 45 | working-directory: ${{ inputs.workdir }} 46 | run: | 47 | (uv run basedpyright ${{ inputs.basedpyright_flags }} || true) | \ 48 | reviewdog -f=rdjson \ 49 | -reporter=${{ inputs.reporter }} \ 50 | -level=${{ inputs.level }} \ 51 | -filter-mode=${{ inputs.filter_mode }} \ 52 | -fail-level=${{ inputs.fail_level }} \ 53 | -name=${{ inputs.tool_name }} \ 54 | ${{ inputs.reviewdog_flags }} 55 | env: 56 | REVIEWDOG_GITHUB_API_TOKEN: ${{ inputs.github_token }} 57 | -------------------------------------------------------------------------------- /src/tux/services/moderation/factory.py: -------------------------------------------------------------------------------- 1 | """Factory for creating moderation service instances. 2 | 3 | This module provides a centralized factory for creating moderation service 4 | instances with proper dependency injection, reducing duplication across 5 | moderation cogs. 6 | """ 7 | 8 | from typing import TYPE_CHECKING 9 | 10 | from tux.services.moderation.case_service import CaseService 11 | from tux.services.moderation.communication_service import CommunicationService 12 | from tux.services.moderation.execution_service import ExecutionService 13 | from tux.services.moderation.moderation_coordinator import ModerationCoordinator 14 | 15 | if TYPE_CHECKING: 16 | from tux.core.bot import Tux 17 | from tux.database.controllers import CaseController 18 | 19 | __all__ = ["ModerationServiceFactory"] 20 | 21 | 22 | class ModerationServiceFactory: 23 | """Factory for creating moderation service instances. 24 | 25 | Centralizes the creation logic for moderation services to ensure 26 | consistent dependency injection across all moderation cogs. 27 | """ 28 | 29 | @staticmethod 30 | def create_coordinator( 31 | bot: "Tux", 32 | case_controller: "CaseController", 33 | ) -> ModerationCoordinator: 34 | """Create a ModerationCoordinator with all required services. 35 | 36 | Parameters 37 | ---------- 38 | bot : Tux 39 | The bot instance for communication service 40 | case_controller : CaseController 41 | The database controller for case management 42 | 43 | Returns 44 | ------- 45 | ModerationCoordinator 46 | Fully initialized moderation coordinator 47 | 48 | Examples 49 | -------- 50 | >>> coordinator = ModerationServiceFactory.create_coordinator( 51 | ... self.bot, self.db.case 52 | ... ) 53 | """ 54 | case_service = CaseService(case_controller) 55 | communication_service = CommunicationService(bot) 56 | execution_service = ExecutionService() 57 | 58 | return ModerationCoordinator( 59 | case_service=case_service, 60 | communication_service=communication_service, 61 | execution_service=execution_service, 62 | ) 63 | -------------------------------------------------------------------------------- /src/tux/modules/moderation/warn.py: -------------------------------------------------------------------------------- 1 | """ 2 | User warning commands for Discord moderation. 3 | 4 | This module provides functionality to issue warnings to Discord server members, 5 | with automatic case tracking and notification systems. 6 | """ 7 | 8 | import discord 9 | from discord.ext import commands 10 | 11 | from tux.core.bot import Tux 12 | from tux.core.checks import requires_command_permission 13 | from tux.core.flags import WarnFlags 14 | from tux.database.models import CaseType as DBCaseType 15 | 16 | from . import ModerationCogBase 17 | 18 | 19 | class Warn(ModerationCogBase): 20 | """Discord cog for issuing warnings to server members.""" 21 | 22 | def __init__(self, bot: Tux) -> None: 23 | """Initialize the Warn cog. 24 | 25 | Parameters 26 | ---------- 27 | bot : Tux 28 | The bot instance to attach this cog to. 29 | """ 30 | super().__init__(bot) 31 | 32 | @commands.hybrid_command( 33 | name="warn", 34 | aliases=["w"], 35 | ) 36 | @commands.guild_only() 37 | @requires_command_permission() 38 | async def warn( 39 | self, 40 | ctx: commands.Context[Tux], 41 | member: discord.Member, 42 | *, 43 | flags: WarnFlags, 44 | ) -> None: 45 | """ 46 | Warn a member from the server. 47 | 48 | Parameters 49 | ---------- 50 | ctx : commands.Context[Tux] 51 | The context in which the command is being invoked. 52 | member : discord.Member 53 | The member to warn. 54 | flags : WarnFlags 55 | The flags for the command. (reason: str, silent: bool) 56 | """ 57 | assert ctx.guild 58 | 59 | # Execute warn with case creation and DM 60 | await self.moderate_user( 61 | ctx=ctx, 62 | case_type=DBCaseType.WARN, 63 | user=member, 64 | reason=flags.reason, 65 | silent=flags.silent, 66 | dm_action="warned", 67 | actions=[], # No Discord API actions needed for warnings 68 | ) 69 | 70 | 71 | async def setup(bot: Tux) -> None: 72 | """Set up the Warn cog. 73 | 74 | Parameters 75 | ---------- 76 | bot : Tux 77 | The bot instance to add the cog to. 78 | """ 79 | await bot.add_cog(Warn(bot)) 80 | -------------------------------------------------------------------------------- /docs/content/support/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Support 3 | icon: material/forum-outline 4 | tags: 5 | - support 6 | --- 7 | 8 | # Support 9 | 10 | !!! warning "Work in progress" 11 | This section is a work in progress. Please help us by contributing to the documentation. 12 | 13 | If you need any assistance with Tux or want to report bugs/leave feedback, we have many support channels available to help you. 14 | 15 | __If this is a security issue, please use the method according to our [Security Policy](https://github.com/allthingslinux/tux/security/policy).__ 16 | 17 | ## GitHub Issues 18 | 19 | The primary method for feedback and bug reporting is through our GitHub Issues page. Go to [our Issues Page](https://github.com/allthingslinux/tux/issues) and check if your issue has already been reported. If not, click on "New Issue" to create an issue and fill out the provided template. 20 | 21 | If you do not have a GitHub account, you can create one for free at [github.com/signup](https://github.com/signup). 22 | 23 | ## Discord Server 24 | 25 | !!! warning 26 | We do not recommend using this method as future people who have the same issue will not be able to find the solution through search engines. 27 | 28 | We have an active Discord community where you can ask questions, report issues, and get help from both the Tux team and other users. 29 | 30 | - __Invite Link__: [Join our Discord Server](https://discord.gg/gpmSjcjQxg) 31 | 32 | ### Methods to get support on Discord 33 | 34 | - __#tux-support__ channel 35 | - You can ping individual team members for help, but please do not spam. 36 | - You can open a ticket with `/ticket` if this is a potentially sensitive issue. 37 | 38 | ### Methods to suggest feedback on Discord 39 | 40 | - Post in __#general-chat__ channel 41 | - You can ping individual team members for specific thoughts, but please do not spam. 42 | - You can open a ticket with `/ticket` if you want to provide feedback privately. 43 | 44 | ## All Things Linux 45 | 46 | If this issue is a more general Linux question, consider asking in the All Things Linux community: 47 | 48 | - __Website__: [allthingslinux.org](https://allthingslinux.org) 49 | - __Discord__: [discord.gg/linux](https://discord.gg/linux) 50 | 51 | If you want to give feedback we would love to hear your thoughts! We have multiple ways for you to provide feedback. 52 | 53 | 54 | -------------------------------------------------------------------------------- /typings/py_pglite/sqlalchemy/manager.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | from typing import Any 6 | from ..manager import PGliteManager 7 | 8 | """SQLAlchemy-specific manager for py-pglite. 9 | 10 | Extends the core PGliteManager with SQLAlchemy-specific functionality. 11 | """ 12 | class SQLAlchemyPGliteManager(PGliteManager): 13 | """PGlite manager with SQLAlchemy-specific functionality. 14 | 15 | Extends the core PGliteManager with methods that require SQLAlchemy. 16 | Use this manager when you need SQLAlchemy integration. 17 | """ 18 | def __enter__(self) -> SQLAlchemyPGliteManager: 19 | """Override to return correct type for type checking.""" 20 | ... 21 | 22 | def get_engine(self, **engine_kwargs: Any) -> Any: 23 | """Get SQLAlchemy engine connected to PGlite. 24 | 25 | NOTE: This method requires SQLAlchemy to be installed. 26 | 27 | IMPORTANT: Returns a shared engine instance to prevent connection timeouts. 28 | PGlite's socket server can only handle 1 connection at a time, so multiple 29 | engines would cause psycopg.errors.ConnectionTimeout. The shared engine 30 | architecture ensures all database operations use the same connection. 31 | 32 | Args: 33 | **engine_kwargs: Additional arguments for create_engine 34 | 35 | Returns: 36 | SQLAlchemy Engine connected to PGlite (shared instance) 37 | 38 | Raises: 39 | ImportError: If SQLAlchemy is not installed 40 | RuntimeError: If PGlite server is not running 41 | """ 42 | ... 43 | 44 | def wait_for_ready(self, max_retries: int = ..., delay: float = ...) -> bool: 45 | """Wait for database to be ready and responsive. 46 | 47 | NOTE: This method requires SQLAlchemy to be installed. 48 | 49 | Args: 50 | max_retries: Maximum number of connection attempts 51 | delay: Delay between attempts in seconds 52 | 53 | Returns: 54 | True if database becomes ready, False otherwise 55 | 56 | Raises: 57 | ImportError: If SQLAlchemy is not installed 58 | """ 59 | ... 60 | 61 | def stop(self) -> None: 62 | """Stop the PGlite server with proper SQLAlchemy cleanup.""" 63 | ... 64 | 65 | 66 | 67 | __all__ = ["SQLAlchemyPGliteManager"] 68 | -------------------------------------------------------------------------------- /docs/content/reference/sbom.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Software Bill of Materials (SBOM) 3 | hide: 4 | - toc 5 | tags: 6 | - reference 7 | - sbom 8 | - dependencies 9 | --- 10 | --- 11 | 12 | # Software Bill of Materials (SBOM) 13 | 14 | !!! warning "Work in progress" 15 | This section is a work in progress. Please help us by contributing to the documentation. 16 | 17 | This page provides a comprehensive list of all dependencies used in Tux, including their licenses and versions. This information is essential for software supply chain security and legal compliance. 18 | 19 | ## Dependencies 20 | 21 | ::sbom 22 | base_indent: 2 23 | groups: dev, test, docs, types 24 | 25 | ## License Information 26 | 27 | The license information above is automatically generated from the project's dependencies defined in `pyproject.toml`. Each package entry includes: 28 | 29 | - **Package Name** - The name of the dependency 30 | - **License** - The license(s) under which the package is distributed 31 | - **Version** - The version checked during documentation build 32 | - **Author** - The package author/maintainer 33 | 34 | ## License Compliance 35 | 36 | Tux is licensed under the **GPL-3.0-or-later** license. All dependencies listed above are compatible with this license. If you notice any license compatibility issues, please [report them](https://github.com/allthingslinux/tux/issues). 37 | 38 | ## Security 39 | 40 | For security concerns related to dependencies: 41 | 42 | - Review [GitHub Security Advisories](https://github.com/allthingslinux/tux/security/advisories) 43 | - Report security issues via [GitHub Security](https://github.com/allthingslinux/tux/security) 44 | 45 | ## Updating Dependencies 46 | 47 | Dependencies are managed using `uv` and locked in `uv.lock`. To update dependencies: 48 | 49 | ```bash 50 | # Update all dependencies 51 | uv sync --upgrade 52 | 53 | # Update a specific dependency 54 | uv sync --upgrade-package 55 | ``` 56 | 57 | **Automated Updates**: Tux uses [Renovate](./renovate.md) to automatically create pull requests for dependency updates. This helps keep dependencies up-to-date and secure with minimal manual intervention. 58 | 59 | ## Related Documentation 60 | 61 | - **[Renovate](./renovate.md)** - Automated dependency updates and Renovate configuration 62 | - **[Configuration Reference](./env.md)** - Configuration options 63 | - **[CLI Reference](./cli.md)** - Command-line tools 64 | -------------------------------------------------------------------------------- /tests/fixtures/test_data_fixtures.py: -------------------------------------------------------------------------------- 1 | """Test data fixtures for consistent test data.""" 2 | 3 | import pytest 4 | from typing import Any 5 | from loguru import logger 6 | 7 | from tux.database.controllers import GuildConfigController, GuildController 8 | 9 | # Test constants 10 | TEST_GUILD_ID = 123456789012345678 11 | TEST_USER_ID = 987654321098765432 12 | TEST_CHANNEL_ID = 876543210987654321 13 | TEST_MODERATOR_ID = 555666777888999000 14 | 15 | 16 | @pytest.fixture(scope="function") 17 | async def sample_guild(guild_controller: GuildController) -> Any: 18 | """Sample guild for testing.""" 19 | logger.info("🔧 Creating sample guild") 20 | guild = await guild_controller.insert_guild_by_id(TEST_GUILD_ID) 21 | logger.info(f"✅ Created sample guild with ID: {guild.id}") 22 | return guild 23 | 24 | 25 | @pytest.fixture(scope="function") 26 | async def sample_guild_with_config( 27 | guild_controller: GuildController, 28 | guild_config_controller: GuildConfigController, 29 | ) -> dict[str, Any]: 30 | """Sample guild with config for testing.""" 31 | logger.info("🔧 Creating sample guild with config") 32 | 33 | # Create guild 34 | guild = await guild_controller.insert_guild_by_id(TEST_GUILD_ID) 35 | 36 | # Create config 37 | config = await guild_config_controller.insert_guild_config( 38 | guild_id=TEST_GUILD_ID, 39 | prefix="!", 40 | ) 41 | 42 | result = {"guild": guild, "config": config} 43 | logger.info(f"✅ Created sample guild with config: {guild.id}") 44 | return result 45 | 46 | 47 | def validate_guild_structure(guild: Any) -> bool: 48 | """Validate guild model structure and required fields.""" 49 | return ( 50 | hasattr(guild, "id") and 51 | hasattr(guild, "case_count") and 52 | hasattr(guild, "guild_joined_at") and 53 | isinstance(guild.id, int) and 54 | isinstance(guild.case_count, int) 55 | ) 56 | 57 | 58 | def validate_guild_config_structure(config: Any) -> bool: 59 | """Validate guild config model structure and required fields.""" 60 | return ( 61 | hasattr(config, "id") and 62 | hasattr(config, "prefix") and 63 | isinstance(config.id, int) and 64 | (config.prefix is None or isinstance(config.prefix, str)) 65 | ) 66 | 67 | 68 | def validate_relationship_integrity(guild: Any, config: Any) -> bool: 69 | """Validate relationship integrity between guild and config.""" 70 | return guild.id == config.id 71 | -------------------------------------------------------------------------------- /src/tux/modules/moderation/kick.py: -------------------------------------------------------------------------------- 1 | """ 2 | Kick moderation command for Tux Bot. 3 | 4 | This module provides the kick command functionality, allowing server 5 | moderators to kick users from the server. 6 | """ 7 | 8 | import discord 9 | from discord.ext import commands 10 | 11 | from tux.core.bot import Tux 12 | from tux.core.checks import requires_command_permission 13 | from tux.core.flags import KickFlags 14 | from tux.database.models import CaseType as DBCaseType 15 | 16 | from . import ModerationCogBase 17 | 18 | 19 | class Kick(ModerationCogBase): 20 | """Kick command cog for moderating server members.""" 21 | 22 | def __init__(self, bot: Tux) -> None: 23 | """Initialize the Kick cog. 24 | 25 | Parameters 26 | ---------- 27 | bot : Tux 28 | The bot instance to initialize the cog with. 29 | """ 30 | super().__init__(bot) 31 | 32 | @commands.hybrid_command( 33 | name="kick", 34 | aliases=["k"], 35 | ) 36 | @commands.guild_only() 37 | @requires_command_permission() 38 | async def kick( 39 | self, 40 | ctx: commands.Context[Tux], 41 | member: discord.Member, 42 | *, 43 | flags: KickFlags, 44 | ) -> None: 45 | """ 46 | Kick a member from the server. 47 | 48 | Parameters 49 | ---------- 50 | ctx : commands.Context[Tux] 51 | The context in which the command is being invoked. 52 | member : discord.Member 53 | The member to kick. 54 | flags : KickFlags 55 | The flags for the command. (reason: str, silent: bool) 56 | """ 57 | assert ctx.guild 58 | 59 | # Permission checks are handled by the @requires_command_permission() decorator 60 | # Additional validation will be handled by the ModerationCoordinator service 61 | 62 | # Execute kick with case creation and DM 63 | await self.moderate_user( 64 | ctx=ctx, 65 | case_type=DBCaseType.KICK, 66 | user=member, 67 | reason=flags.reason, 68 | silent=flags.silent, 69 | dm_action="kicked", 70 | actions=[(lambda: member.kick(reason=flags.reason), type(None))], 71 | ) 72 | 73 | 74 | async def setup(bot: Tux) -> None: 75 | """Set up the Kick cog. 76 | 77 | Parameters 78 | ---------- 79 | bot : Tux 80 | The bot instance to add the cog to. 81 | """ 82 | await bot.add_cog(Kick(bot)) 83 | -------------------------------------------------------------------------------- /typings/pydantic_settings_export/utils.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | This type stub file was generated by pyright. 3 | """ 4 | 5 | import argparse 6 | from collections.abc import Sequence 7 | from typing import Any, Optional, Union 8 | from pydantic_settings import BaseSettings 9 | 10 | __all__ = ("make_pretty_md_table", "make_pretty_md_table_from_dict") 11 | MARKDOWN_PIPE_RE = ... 12 | def make_pretty_md_table(headers: list[str], rows: list[list[str]]) -> str: 13 | """Make a pretty Markdown table with column alignment. 14 | 15 | :param headers: The header of the table. 16 | :param rows: The rows of the table. 17 | :return: The prettied Markdown table. 18 | """ 19 | ... 20 | 21 | def make_pretty_md_table_from_dict(data: list[dict[str, Optional[str]]], headers: Optional[list[str]] = ...) -> str: 22 | """Make a pretty Markdown table with column alignment from a list of dictionaries. 23 | 24 | :param data: The rows of the table as dictionaries. 25 | :param headers: The headers of the table. 26 | :return: The prettied Markdown table. 27 | """ 28 | ... 29 | 30 | def q(s: Any) -> str: 31 | """Add quotes around the string.""" 32 | ... 33 | 34 | class ObjectImportAction(argparse.Action): 35 | """Import the object from the module.""" 36 | @staticmethod 37 | def callback(obj: Any) -> Any: 38 | """Check if the object is a settings class.""" 39 | ... 40 | 41 | @staticmethod 42 | def import_obj(value: str) -> Any: 43 | """Import the object from the module. 44 | 45 | :param value: The value in the format is 'module:class'. 46 | :raise ValueError: If the value is not in the format 'module:class'. 47 | :raise ValueError: If the class is not in the module. 48 | :raise ModuleNotFoundError: If the module is not found. 49 | :return: The imported object. 50 | """ 51 | ... 52 | 53 | def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: Optional[Union[str, Sequence[Any]]], option_string: Optional[str] = ...) -> None: 54 | """Import the object from the module.""" 55 | ... 56 | 57 | 58 | 59 | class MissingSettingsError(ValueError): 60 | """Raised when the settings are missing.""" 61 | def __init__(self, missing: dict[Union[str, int], str], settings_path: str = ...) -> None: 62 | ... 63 | 64 | 65 | 66 | def import_settings_from_string(value: str) -> BaseSettings: 67 | """Import the settings from the string.""" 68 | ... 69 | 70 | -------------------------------------------------------------------------------- /src/tux/modules/snippets/delete_snippet.py: -------------------------------------------------------------------------------- 1 | """ 2 | Delete snippet commands. 3 | 4 | This module provides functionality for deleting existing code snippets 5 | from Discord guilds with ownership and permission validation. 6 | """ 7 | 8 | from discord.ext import commands 9 | from loguru import logger 10 | 11 | from tux.core.bot import Tux 12 | 13 | from . import SnippetsBaseCog 14 | 15 | 16 | class DeleteSnippet(SnippetsBaseCog): 17 | """Discord cog for deleting snippets.""" 18 | 19 | def __init__(self, bot: Tux) -> None: 20 | """Initialize the delete snippet cog. 21 | 22 | Parameters 23 | ---------- 24 | bot : Tux 25 | The bot instance to attach this cog to. 26 | """ 27 | super().__init__(bot) 28 | # Usage is auto-generated by BaseCog 29 | 30 | @commands.command( 31 | name="deletesnippet", 32 | aliases=["ds"], 33 | ) 34 | @commands.guild_only() 35 | async def delete_snippet(self, ctx: commands.Context[Tux], name: str) -> None: 36 | """Delete a snippet by name. 37 | 38 | Checks for ownership and lock status before deleting. 39 | 40 | Parameters 41 | ---------- 42 | ctx : commands.Context[Tux] 43 | The context of the command. 44 | name : str 45 | The name of the snippet to delete. 46 | """ 47 | assert ctx.guild 48 | 49 | # Fetch the snippet, send error if not found 50 | snippet = await self._get_snippet_or_error(ctx, name) 51 | if not snippet: 52 | return 53 | 54 | # Check permissions (role, ban, lock, ownership) 55 | can_delete, reason = await self.snippet_check( 56 | ctx, 57 | snippet_locked=snippet.locked, 58 | snippet_user_id=snippet.snippet_user_id, 59 | ) 60 | 61 | if not can_delete: 62 | await self.send_snippet_error(ctx, description=reason) 63 | return 64 | 65 | # Delete the snippet 66 | if snippet.id is not None: 67 | await self.db.snippet.delete_snippet_by_id(snippet.id) 68 | else: 69 | await ctx.send("Error: Snippet ID is invalid.", ephemeral=True) 70 | return 71 | 72 | await ctx.send("Snippet deleted.", ephemeral=True) 73 | 74 | logger.info(f"{ctx.author} deleted snippet '{name}'. Override: {reason}") 75 | 76 | 77 | async def setup(bot: Tux) -> None: 78 | """Load the DeleteSnippet cog.""" 79 | await bot.add_cog(DeleteSnippet(bot)) 80 | -------------------------------------------------------------------------------- /src/tux/modules/guild/member_count.py: -------------------------------------------------------------------------------- 1 | """ 2 | Guild member count display commands. 3 | 4 | This module provides commands to display member statistics for Discord servers, 5 | including total member count, human users, and bot counts. 6 | """ 7 | 8 | from discord.ext import commands 9 | 10 | from tux.core.base_cog import BaseCog 11 | from tux.core.bot import Tux 12 | from tux.ui.embeds import EmbedCreator 13 | 14 | 15 | class MemberCount(BaseCog): 16 | """Discord cog for displaying guild member statistics.""" 17 | 18 | def __init__(self, bot: Tux) -> None: 19 | """Initialize the member count cog. 20 | 21 | Parameters 22 | ---------- 23 | bot : Tux 24 | The bot instance to attach this cog to. 25 | """ 26 | super().__init__(bot) 27 | 28 | @commands.hybrid_command( 29 | name="membercount", 30 | aliases=["mc", "members"], 31 | description="Shows server member count", 32 | ) 33 | @commands.guild_only() 34 | async def member_count(self, ctx: commands.Context[Tux]) -> None: 35 | """ 36 | Show the member count for the server. 37 | 38 | Parameters 39 | ---------- 40 | ctx : commands.Context[Tux] 41 | The discord context object. 42 | """ 43 | assert ctx.guild 44 | 45 | # Get the member count for the server (total members) 46 | members = ctx.guild.member_count 47 | # Get the number of humans in the server (subtract bots from total members) 48 | humans = sum(not member.bot for member in ctx.guild.members) 49 | # Get the number of bots in the server (subtract humans from total members) 50 | bots = sum(member.bot for member in ctx.guild.members if member.bot) 51 | 52 | embed = EmbedCreator.create_embed( 53 | bot=self.bot, 54 | embed_type=EmbedCreator.INFO, 55 | user_name=ctx.author.name, 56 | user_display_avatar=ctx.author.display_avatar.url, 57 | title="Member Count", 58 | ) 59 | 60 | embed.add_field(name="Members", value=str(members), inline=True) 61 | embed.add_field(name="Humans", value=str(humans), inline=True) 62 | embed.add_field(name="Bots", value=str(bots), inline=True) 63 | 64 | await ctx.send(embed=embed) 65 | 66 | 67 | async def setup(bot: Tux) -> None: 68 | """Set up the MemberCount cog. 69 | 70 | Parameters 71 | ---------- 72 | bot : Tux 73 | The bot instance to add the cog to. 74 | """ 75 | await bot.add_cog(MemberCount(bot)) 76 | -------------------------------------------------------------------------------- /tests/unit/test_migration_url_conversion.py: -------------------------------------------------------------------------------- 1 | """ 2 | Migration URL Conversion Tests 3 | 4 | Tests that verify async database URLs are correctly converted to sync format 5 | for Alembic compatibility. This is critical for the migration system to work 6 | with async database drivers. 7 | 8 | Note: We test the URL conversion logic directly rather than importing from 9 | env.py, as env.py has module-level code that requires Alembic context to be 10 | established, which isn't available during test collection. 11 | """ 12 | 13 | import pytest 14 | 15 | 16 | class TestURLConversion: 17 | """Test async to sync URL conversion for Alembic.""" 18 | 19 | def test_async_url_conversion_pattern(self): 20 | """Test that async URL pattern is correctly identified.""" 21 | async_url = "postgresql+psycopg_async://user:pass@host:5432/db" 22 | sync_url = "postgresql+psycopg://user:pass@host:5432/db" 23 | 24 | # Test conversion logic 25 | converted = async_url.replace("postgresql+psycopg_async://", "postgresql+psycopg://", 1) 26 | assert converted == sync_url 27 | 28 | def test_sync_url_unchanged(self): 29 | """Test that sync URLs are not modified.""" 30 | sync_url = "postgresql+psycopg://user:pass@host:5432/db" 31 | 32 | # Should remain unchanged 33 | converted = sync_url.replace("postgresql+psycopg_async://", "postgresql+psycopg://", 1) 34 | assert converted == sync_url 35 | 36 | def test_other_driver_urls_unchanged(self): 37 | """Test that URLs with other drivers are not modified.""" 38 | other_urls = [ 39 | "postgresql://user:pass@host:5432/db", 40 | "postgresql+asyncpg://user:pass@host:5432/db", 41 | "sqlite:///path/to/db", 42 | ] 43 | 44 | for url in other_urls: 45 | converted = url.replace("postgresql+psycopg_async://", "postgresql+psycopg://", 1) 46 | assert converted == url, f"URL should not be modified: {url}" 47 | 48 | @pytest.mark.skip(reason="Requires mocking CONFIG.database_url") 49 | def test_offline_mode_converts_url(self): 50 | """Test that offline mode converts async URLs.""" 51 | # This would require mocking CONFIG.database_url 52 | # For now, we test the conversion logic directly 53 | pass 54 | 55 | @pytest.mark.skip(reason="Requires mocking CONFIG.database_url") 56 | def test_online_mode_converts_url(self): 57 | """Test that online mode converts async URLs.""" 58 | # This would require mocking CONFIG.database_url and database connection 59 | # For now, we test the conversion logic directly 60 | pass 61 | -------------------------------------------------------------------------------- /src/tux/services/sentry/cog.py: -------------------------------------------------------------------------------- 1 | """Sentry integration cog for command tracking and context enrichment.""" 2 | 3 | import discord 4 | from discord.ext import commands 5 | from loguru import logger 6 | 7 | from tux.core.bot import Tux 8 | from tux.services.sentry import ( 9 | set_command_context, 10 | set_user_context, 11 | track_command_end, 12 | track_command_start, 13 | ) 14 | 15 | 16 | class SentryHandler(commands.Cog): 17 | """Handles Sentry context enrichment and command performance tracking.""" 18 | 19 | def __init__(self, bot: Tux) -> None: 20 | """Initialize the Sentry handler cog. 21 | 22 | Parameters 23 | ---------- 24 | bot : Tux 25 | The bot instance to attach the handler to. 26 | """ 27 | self.bot = bot 28 | 29 | @commands.Cog.listener("on_command") 30 | async def on_command(self, ctx: commands.Context[Tux]) -> None: 31 | """Track command start and set context for prefix commands.""" 32 | if ctx.command: 33 | # Set enhanced Sentry context 34 | set_command_context(ctx) 35 | set_user_context(ctx.author) 36 | 37 | # Start performance tracking 38 | track_command_start(ctx.command.qualified_name) 39 | 40 | @commands.Cog.listener("on_command_completion") 41 | async def on_command_completion(self, ctx: commands.Context[Tux]) -> None: 42 | """Track successful command completion.""" 43 | if ctx.command: 44 | track_command_end(ctx.command.qualified_name, success=True) 45 | 46 | @commands.Cog.listener("on_app_command_completion") 47 | async def on_app_command_completion(self, interaction: discord.Interaction) -> None: 48 | """Track successful app command completion.""" 49 | if interaction.command: 50 | # Set context for app commands 51 | set_command_context(interaction) 52 | set_user_context(interaction.user) 53 | 54 | # Track completion (command_type will be determined in track_command_end) 55 | track_command_end(interaction.command.qualified_name, success=True) 56 | 57 | async def cog_load(self) -> None: 58 | """Log when cog is loaded.""" 59 | logger.debug("Sentry handler cog loaded") 60 | 61 | async def cog_unload(self) -> None: 62 | """Log when cog is unloaded.""" 63 | logger.debug("Sentry handler cog unloaded") 64 | 65 | 66 | async def setup(bot: Tux) -> None: 67 | """Cog setup for Sentry handler. 68 | 69 | Parameters 70 | ---------- 71 | bot : Tux 72 | The bot instance. 73 | """ 74 | await bot.add_cog(SentryHandler(bot)) 75 | -------------------------------------------------------------------------------- /src/tux/database/controllers/base/transaction.py: -------------------------------------------------------------------------------- 1 | """Transaction management for database controllers.""" 2 | 3 | from collections.abc import Awaitable, Callable 4 | from typing import Any, TypeVar 5 | 6 | from sqlalchemy.ext.asyncio import AsyncSession 7 | from sqlmodel import SQLModel 8 | 9 | from tux.database.service import DatabaseService 10 | 11 | ModelT = TypeVar("ModelT", bound=SQLModel) 12 | R = TypeVar("R") 13 | 14 | 15 | class TransactionController[ModelT]: 16 | """Handles transaction and session management.""" 17 | 18 | def __init__(self, model: type[ModelT], db: DatabaseService) -> None: 19 | """Initialize the transaction controller. 20 | 21 | Parameters 22 | ---------- 23 | model : type[ModelT] 24 | The SQLModel to manage transactions for. 25 | db : DatabaseService 26 | The database service instance. 27 | """ 28 | self.model = model 29 | self.db = db 30 | 31 | async def with_session[R]( 32 | self, 33 | operation: Callable[[AsyncSession], Awaitable[R]], 34 | ) -> R: 35 | """ 36 | Execute operation within a session context. 37 | 38 | Returns 39 | ------- 40 | R 41 | The result of the operation. 42 | """ 43 | async with self.db.session() as session: 44 | return await operation(session) 45 | 46 | async def with_transaction[R]( 47 | self, 48 | operation: Callable[[AsyncSession], Awaitable[R]], 49 | ) -> R: 50 | """ 51 | Execute operation within a transaction context. 52 | 53 | Returns 54 | ------- 55 | R 56 | The result of the operation. 57 | """ 58 | async with self.db.session() as session, session.begin(): 59 | return await operation(session) 60 | 61 | async def execute_transaction(self, callback: Callable[[], Any]) -> Any: 62 | """ 63 | Execute a callback within a transaction. 64 | 65 | Returns 66 | ------- 67 | Any 68 | The result of the callback. 69 | """ 70 | async with self.db.session() as session, session.begin(): 71 | return await callback() 72 | 73 | @staticmethod 74 | def safe_get_attr(obj: Any, attr: str, default: Any = None) -> Any: 75 | """ 76 | Safely get attribute from object. 77 | 78 | Returns 79 | ------- 80 | Any 81 | The attribute value, or default if not found. 82 | """ 83 | try: 84 | return getattr(obj, attr, default) 85 | except (AttributeError, TypeError): 86 | return default 87 | --------------------------------------------------------------------------------