feat: dockerize p4bl0t #17
							
								
								
									
										32
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
# Include any files or directories that you don't want to be copied to your
 | 
			
		||||
# container here (e.g., local build artifacts, temporary files, etc.).
 | 
			
		||||
#
 | 
			
		||||
# For more help, visit the .dockerignore file reference guide at
 | 
			
		||||
# https://docs.docker.com/go/build-context-dockerignore/
 | 
			
		||||
 | 
			
		||||
**/.DS_Store
 | 
			
		||||
**/.classpath
 | 
			
		||||
**/.dockerignore
 | 
			
		||||
**/.env
 | 
			
		||||
**/.git
 | 
			
		||||
**/.gitignore
 | 
			
		||||
**/.project
 | 
			
		||||
**/.settings
 | 
			
		||||
**/.toolstarget
 | 
			
		||||
**/.vs
 | 
			
		||||
**/.vscode
 | 
			
		||||
**/*.*proj.user
 | 
			
		||||
**/*.dbmdl
 | 
			
		||||
**/*.jfm
 | 
			
		||||
**/charts
 | 
			
		||||
**/docker-compose*
 | 
			
		||||
**/compose*
 | 
			
		||||
**/Dockerfile*
 | 
			
		||||
**/node_modules
 | 
			
		||||
**/npm-debug.log
 | 
			
		||||
**/secrets.dev.yaml
 | 
			
		||||
**/values.dev.yaml
 | 
			
		||||
/bin
 | 
			
		||||
/target
 | 
			
		||||
LICENSE
 | 
			
		||||
README.md
 | 
			
		||||
@ -1,2 +1 @@
 | 
			
		||||
DISCORD_TOKEN=changeme
 | 
			
		||||
DATABASE_URL=sqlite:p4bl0t.db
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,4 +1,3 @@
 | 
			
		||||
/target
 | 
			
		||||
/.env
 | 
			
		||||
*.db
 | 
			
		||||
/.sqlx/
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								.sqlx/query-5b44991d1514160fa00572e398f0577ad44f839a0470f9eeb89da8b5e77f0e03.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.sqlx/query-5b44991d1514160fa00572e398f0577ad44f839a0470f9eeb89da8b5e77f0e03.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "db_name": "SQLite",
 | 
			
		||||
  "query": "\nINSERT INTO guild_log_channels (guild_id, channel_id)\nVALUES ( ?1, ?2 )",
 | 
			
		||||
  "describe": {
 | 
			
		||||
    "columns": [],
 | 
			
		||||
    "parameters": {
 | 
			
		||||
      "Right": 2
 | 
			
		||||
    },
 | 
			
		||||
    "nullable": []
 | 
			
		||||
  },
 | 
			
		||||
  "hash": "5b44991d1514160fa00572e398f0577ad44f839a0470f9eeb89da8b5e77f0e03"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								.sqlx/query-8444f7b7452a5ace6352aef943274f8a345a958257d896c7658b7700557959ab.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.sqlx/query-8444f7b7452a5ace6352aef943274f8a345a958257d896c7658b7700557959ab.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
{
 | 
			
		||||
  "db_name": "SQLite",
 | 
			
		||||
  "query": "\nSELECT channel_id\nFROM guild_log_channels\nWHERE guild_id = ?1",
 | 
			
		||||
  "describe": {
 | 
			
		||||
    "columns": [
 | 
			
		||||
      {
 | 
			
		||||
        "name": "channel_id",
 | 
			
		||||
        "ordinal": 0,
 | 
			
		||||
        "type_info": "Int64"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "parameters": {
 | 
			
		||||
      "Right": 1
 | 
			
		||||
    },
 | 
			
		||||
    "nullable": [
 | 
			
		||||
      false
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "hash": "8444f7b7452a5ace6352aef943274f8a345a958257d896c7658b7700557959ab"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								.sqlx/query-d6e9f422d6ae29a00658f55165018119d1e13d407266440415dfcc17a97ba00e.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.sqlx/query-d6e9f422d6ae29a00658f55165018119d1e13d407266440415dfcc17a97ba00e.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "db_name": "SQLite",
 | 
			
		||||
  "query": "\nDELETE FROM guild_log_channels\nWHERE guild_id = ?1 AND channel_id = ?2",
 | 
			
		||||
  "describe": {
 | 
			
		||||
    "columns": [],
 | 
			
		||||
    "parameters": {
 | 
			
		||||
      "Right": 2
 | 
			
		||||
    },
 | 
			
		||||
    "nullable": []
 | 
			
		||||
  },
 | 
			
		||||
  "hash": "d6e9f422d6ae29a00658f55165018119d1e13d407266440415dfcc17a97ba00e"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										494
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										494
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -10,12 +10,12 @@ homepage = "https://github.com/phundrak/p4bl0t"
 | 
			
		||||
repository = "https://github.com/phundrak/p4bl0t"
 | 
			
		||||
keywords = ["discord", "bot", "logging"]
 | 
			
		||||
publish = false
 | 
			
		||||
build = "build.rs"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
color-eyre = "0.6.2"
 | 
			
		||||
dotenvy = "0.15.7"
 | 
			
		||||
poise = { version = "0.5.7" }
 | 
			
		||||
sqlx = { version = "0.7.2", features = ["sqlite", "tls-rustls", "runtime-tokio-rustls"] }
 | 
			
		||||
sqlx = { version = "0.7.3", features = ["sqlite", "tls-rustls", "runtime-tokio-rustls"] }
 | 
			
		||||
tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread"] }
 | 
			
		||||
tracing = "0.1.40"
 | 
			
		||||
tracing-subscriber = "0.3.18"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										93
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
			
		||||
# syntax=docker/dockerfile:1
 | 
			
		||||
 | 
			
		||||
# Comments are provided throughout this file to help you get started.
 | 
			
		||||
# If you need more help, visit the Dockerfile reference guide at
 | 
			
		||||
# https://docs.docker.com/go/dockerfile-reference/
 | 
			
		||||
 | 
			
		||||
ARG RUST_VERSION=1.73.0
 | 
			
		||||
ARG APP_NAME=p4bl0t
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# xx is a helper for cross-compilation.
 | 
			
		||||
# See https://github.com/tonistiigi/xx/ for more information.
 | 
			
		||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.3.0 AS xx
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Create a stage for building the application.
 | 
			
		||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-alpine AS build
 | 
			
		||||
ARG APP_NAME
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
 | 
			
		||||
# Copy cross compilation utilities from the xx stage.
 | 
			
		||||
COPY --from=xx / /
 | 
			
		||||
 | 
			
		||||
# Install host build dependencies.
 | 
			
		||||
RUN apk add --no-cache clang lld musl-dev git file
 | 
			
		||||
 | 
			
		||||
# This is the architecture you’re building for, which is passed in by the builder.
 | 
			
		||||
# Placing it here allows the previous steps to be cached across architectures.
 | 
			
		||||
ARG TARGETPLATFORM
 | 
			
		||||
 | 
			
		||||
# Install cross compilation build dependencies.
 | 
			
		||||
RUN xx-apk add --no-cache musl-dev gcc
 | 
			
		||||
 | 
			
		||||
# Build the application.
 | 
			
		||||
# Leverage a cache mount to /usr/local/cargo/registry/
 | 
			
		||||
# for downloaded dependencies, a cache mount to /usr/local/cargo/git/db
 | 
			
		||||
# for git repository dependencies, and a cache mount to /app/target/ for
 | 
			
		||||
# compiled dependencies which will speed up subsequent builds.
 | 
			
		||||
# Leverage a bind mount to the src directory to avoid having to copy the
 | 
			
		||||
# source code into the container. Once built, copy the executable to an
 | 
			
		||||
# output directory before the cache mounted /app/target is unmounted.
 | 
			
		||||
RUN --mount=type=bind,source=src,target=src \
 | 
			
		||||
    --mount=type=bind,source=Cargo.toml,target=Cargo.toml \
 | 
			
		||||
    --mount=type=bind,source=Cargo.lock,target=Cargo.lock \
 | 
			
		||||
    --mount=type=bind,source=build.rs,target=build.rs \
 | 
			
		||||
    --mount=type=bind,source=.sqlx,target=.sqlx \
 | 
			
		||||
    --mount=type=bind,source=migrations,target=migrations \
 | 
			
		||||
    --mount=type=cache,target=/app/target/,id=rust-cache-${APP_NAME}-${TARGETPLATFORM} \
 | 
			
		||||
    --mount=type=cache,target=/usr/local/cargo/git/db \
 | 
			
		||||
    --mount=type=cache,target=/usr/local/cargo/registry/ \
 | 
			
		||||
    <<EOF
 | 
			
		||||
set -e
 | 
			
		||||
# xx-cargo build --locked --release --target-dir ./target
 | 
			
		||||
xx-cargo build --locked --target-dir ./target
 | 
			
		||||
cp ./target/$(xx-cargo --print-target-triple)/debug/$APP_NAME /bin/server
 | 
			
		||||
xx-verify /bin/server
 | 
			
		||||
EOF
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
# Create a new stage for running the application that contains the minimal
 | 
			
		||||
# runtime dependencies for the application. This often uses a different base
 | 
			
		||||
# image from the build stage where the necessary files are copied from the build
 | 
			
		||||
# stage.
 | 
			
		||||
#
 | 
			
		||||
# The example below uses the alpine image as the foundation for running the app.
 | 
			
		||||
# By specifying the "3.18" tag, it will use version 3.18 of alpine. If
 | 
			
		||||
# reproducability is important, consider using a digest
 | 
			
		||||
# (e.g., alpine@sha256:664888ac9cfd28068e062c991ebcff4b4c7307dc8dd4df9e728bedde5c449d91).
 | 
			
		||||
FROM alpine:3.18 AS final
 | 
			
		||||
 | 
			
		||||
# Create a non-privileged user that the app will run under.
 | 
			
		||||
# See https://docs.docker.com/go/dockerfile-user-best-practices/
 | 
			
		||||
ARG UID=10001
 | 
			
		||||
RUN adduser \
 | 
			
		||||
    --disabled-password \
 | 
			
		||||
    --gecos "" \
 | 
			
		||||
    --home "/nonexistent" \
 | 
			
		||||
    --shell "/sbin/nologin" \
 | 
			
		||||
    --no-create-home \
 | 
			
		||||
    --uid "${UID}" \
 | 
			
		||||
    appuser
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
RUN chown -R appuser /app
 | 
			
		||||
USER appuser
 | 
			
		||||
 | 
			
		||||
# Copy the executable from the "build" stage.
 | 
			
		||||
COPY --from=build /bin/server /bin/
 | 
			
		||||
 | 
			
		||||
# Expose the port that the application listens on.
 | 
			
		||||
# EXPOSE 8080
 | 
			
		||||
 | 
			
		||||
# What the container should run when it is started.
 | 
			
		||||
CMD ["/bin/server"]
 | 
			
		||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							@ -3,11 +3,24 @@
 | 
			
		||||
p4bl0t is a simple logging bot for Discord written in Rust.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
In order to run p4bl0t, head over to your [developer
 | 
			
		||||
### Preparation
 | 
			
		||||
In order to run p4bl0t, you will need a Discord token with which your
 | 
			
		||||
bot will authenticate. Head over to your [developer
 | 
			
		||||
portal](https://discord.com/developers) on Discord’s website, and
 | 
			
		||||
create a bot there. Then, copy the `.env.example` file to a `.env`
 | 
			
		||||
file and fill in the details.
 | 
			
		||||
create a bot there. You will be able to get the bot’s token there.
 | 
			
		||||
 | 
			
		||||
### Docker
 | 
			
		||||
The easiest way to run p4bl0t is using Docker. Copy
 | 
			
		||||
`docker-compose.example.yml` to `docker-compose.yml` and modify the
 | 
			
		||||
`DISCORD_TOKEN` variable.
 | 
			
		||||
 | 
			
		||||
Then, you can simply run
 | 
			
		||||
```sh
 | 
			
		||||
docker compose up # or docker-compose on some machines
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Building and running it yourself
 | 
			
		||||
Copy the `.env.example` file to a `.env` file and fill in the details.
 | 
			
		||||
```sh
 | 
			
		||||
cp .env.example .env
 | 
			
		||||
emacs .env
 | 
			
		||||
@ -27,7 +40,6 @@ cargo install sqlx-cli
 | 
			
		||||
 | 
			
		||||
Setup your SQLite database.
 | 
			
		||||
```sh
 | 
			
		||||
export DATABASE_URL=<your-database-url> # should be the same as in the .env file
 | 
			
		||||
sqlx database create
 | 
			
		||||
sqlx migrate run
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								build.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
// generated by `sqlx migrate build-script`
 | 
			
		||||
fn main() {
 | 
			
		||||
    // trigger recompilation when a new migration is added
 | 
			
		||||
    println!("cargo:rerun-if-changed=migrations");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								docker-compose.example.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docker-compose.example.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
services:
 | 
			
		||||
  p4bl0t:
 | 
			
		||||
    build:
 | 
			
		||||
      context: .
 | 
			
		||||
      target: final
 | 
			
		||||
    environment:
 | 
			
		||||
      DISCORD_TOKEN: changeme
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./p4bl0t.db:/app/p4bl0t.db
 | 
			
		||||
@ -1,10 +1,8 @@
 | 
			
		||||
#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
 | 
			
		||||
 | 
			
		||||
use std::env;
 | 
			
		||||
 | 
			
		||||
use poise::serenity_prelude::{ChannelId, GuildId};
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
use tracing::error;
 | 
			
		||||
use sqlx::{migrate::MigrateDatabase, Sqlite, SqlitePool};
 | 
			
		||||
use tracing::{error, info, debug};
 | 
			
		||||
 | 
			
		||||
pub type Result<T> = ::std::result::Result<T, sqlx::Error>;
 | 
			
		||||
 | 
			
		||||
@ -16,22 +14,23 @@ impl Database {
 | 
			
		||||
    /// The Sqlite database should already exist and have its
 | 
			
		||||
    /// migrations already executed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// Panics if the environment variable `DATABASE_URL` is not set.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error if the Sqlite pool fails to
 | 
			
		||||
    /// create.
 | 
			
		||||
    pub async fn new() -> Result<Self> {
 | 
			
		||||
        Ok(Self(
 | 
			
		||||
            SqlitePool::connect(
 | 
			
		||||
                &env::var("DATABASE_URL")
 | 
			
		||||
                    .expect("Missing enviroment variable DATABASE_URL"),
 | 
			
		||||
            )
 | 
			
		||||
            .await?,
 | 
			
		||||
        ))
 | 
			
		||||
        let url = "sqlite:p4bl0t.db";
 | 
			
		||||
        if !Sqlite::database_exists(url).await? {
 | 
			
		||||
            info!("Creating database");
 | 
			
		||||
            Sqlite::create_database(url).await?;
 | 
			
		||||
            info!("Database created");
 | 
			
		||||
        }
 | 
			
		||||
        debug!("Getting pool connection");
 | 
			
		||||
        let pool = SqlitePool::connect(url).await?;
 | 
			
		||||
        info!("Running migrations");
 | 
			
		||||
        sqlx::migrate!().run(&pool).await?;
 | 
			
		||||
        debug!("Database initialized");
 | 
			
		||||
        Ok(Self(pool))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return from database all channels registered as loggers for a
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,15 @@
 | 
			
		||||
#![warn(clippy::style, clippy::pedantic)]
 | 
			
		||||
 | 
			
		||||
mod utils;
 | 
			
		||||
mod db;
 | 
			
		||||
mod discord;
 | 
			
		||||
mod utils;
 | 
			
		||||
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    dotenvy::dotenv()?;
 | 
			
		||||
    color_eyre::install()?;
 | 
			
		||||
    utils::setup_logging();
 | 
			
		||||
    color_eyre::install()?;
 | 
			
		||||
 | 
			
		||||
    let bot = discord::make_bot();
 | 
			
		||||
    bot.run().await?;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user