Compare commits
40 Commits
@ -0,0 +1,39 @@
|
|||||||
|
version: 1
|
||||||
|
when:
|
||||||
|
- event: [pull_request]
|
||||||
|
- event: push
|
||||||
|
branch:
|
||||||
|
- ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- release/*
|
||||||
|
- fix/*
|
||||||
|
steps:
|
||||||
|
test:
|
||||||
|
image: rust:alpine
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache --force-overwrite \
|
||||||
|
build-base \
|
||||||
|
openssl-dev \
|
||||||
|
libopusenc-dev \
|
||||||
|
libpq-dev \
|
||||||
|
curl \
|
||||||
|
bash
|
||||||
|
- rustup default stable
|
||||||
|
- rustup component add clippy --toolchain stable-x86_64-unknown-linux-musl
|
||||||
|
- cargo clippy
|
||||||
|
- cargo test --verbose --package bot-coreutils
|
||||||
|
- cargo test --verbose --package bot-database
|
||||||
|
- cargo test --verbose
|
||||||
|
|
||||||
|
build:
|
||||||
|
image: rust:alpine
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache --force-overwrite \
|
||||||
|
build-base \
|
||||||
|
openssl-dev \
|
||||||
|
libopusenc-dev \
|
||||||
|
libpq-dev \
|
||||||
|
curl \
|
||||||
|
bash
|
||||||
|
- cargo build
|
||||||
|
when:
|
||||||
|
- event: [pull_request]
|
@ -0,0 +1,20 @@
|
|||||||
|
version: 1
|
||||||
|
when:
|
||||||
|
- event: [tag]
|
||||||
|
branch:
|
||||||
|
- ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
steps:
|
||||||
|
build:
|
||||||
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
secrets: [forgejo_token]
|
||||||
|
settings:
|
||||||
|
dockerfile: Containerfile
|
||||||
|
tag: ${CI_COMMIT_TAG##v}
|
||||||
|
repo: git.trivernis.net/trivernis/2b-rs
|
||||||
|
registry: git.trivernis.net
|
||||||
|
platforms: linux/amd64
|
||||||
|
username:
|
||||||
|
from_secret: forgejo_id
|
||||||
|
password:
|
||||||
|
from_secret: forgejo_token
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@
|
|||||||
|
ARG BASE_IMAGE=docker.io/alpine:edge
|
||||||
|
|
||||||
|
FROM ${BASE_IMAGE} AS build_base
|
||||||
|
RUN apk update
|
||||||
|
RUN apk add --no-cache --force-overwrite \
|
||||||
|
build-base \
|
||||||
|
openssl-dev \
|
||||||
|
libopusenc-dev \
|
||||||
|
libpq-dev \
|
||||||
|
curl \
|
||||||
|
bash
|
||||||
|
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
|
||||||
|
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||||
|
RUN rm -rf /var/lib/{cache,log}/ /var/cache
|
||||||
|
|
||||||
|
FROM build_base AS builder
|
||||||
|
ENV RUSTFLAGS="-C target-feature=-crt-static"
|
||||||
|
WORKDIR /usr/src
|
||||||
|
RUN cargo new tobi
|
||||||
|
WORKDIR /usr/src/tobi
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY src ./src
|
||||||
|
COPY bot-coreutils ./bot-coreutils
|
||||||
|
COPY bot-database ./bot-database
|
||||||
|
RUN cargo build --release
|
||||||
|
RUN mkdir /tmp/tobi
|
||||||
|
RUN cp target/release/tobi-rs /tmp/tobi/
|
||||||
|
|
||||||
|
FROM ${BASE_IMAGE} AS runtime-base
|
||||||
|
RUN apk update
|
||||||
|
RUN apk add --no-cache --force-overwrite \
|
||||||
|
openssl \
|
||||||
|
libopusenc \
|
||||||
|
libpq \
|
||||||
|
python3 \
|
||||||
|
py3-pip \
|
||||||
|
qalc \
|
||||||
|
ffmpeg \
|
||||||
|
bash
|
||||||
|
RUN pip3 install yt-dlp --break-system-packages
|
||||||
|
RUN rm -rf /var/lib/{cache,log}/ /var/cache
|
||||||
|
|
||||||
|
FROM runtime-base
|
||||||
|
COPY --from=builder /tmp/tobi/tobi-rs .
|
||||||
|
ENTRYPOINT ["/tobi-rs"]
|
@ -1,38 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1.0-experimental
|
|
||||||
|
|
||||||
ARG QALCULATE_VERSION=4.1.1
|
|
||||||
ARG DEBIAN_RELEASE=bullseye
|
|
||||||
|
|
||||||
FROM rust:slim-${DEBIAN_RELEASE} AS builder
|
|
||||||
RUN apt-get update
|
|
||||||
RUN apt-get install -y build-essential libssl-dev libopus-dev libpq-dev pkg-config
|
|
||||||
WORKDIR /usr/src
|
|
||||||
RUN USER=root cargo new tobi
|
|
||||||
WORKDIR /usr/src/tobi
|
|
||||||
COPY Cargo.toml Cargo.lock ./
|
|
||||||
COPY src ./src
|
|
||||||
COPY bot-coreutils ./bot-coreutils
|
|
||||||
COPY bot-database ./bot-database
|
|
||||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
|
||||||
--mount=type=cache,target=target \
|
|
||||||
cargo build --release
|
|
||||||
RUN mkdir /tmp/tobi
|
|
||||||
RUN --mount=type=cache,target=target cp target/release/tobi-rs /tmp/tobi/
|
|
||||||
|
|
||||||
FROM bitnami/minideb:${DEBIAN_RELEASE} AS qalculate-builder
|
|
||||||
ARG QALCULATE_VERSION
|
|
||||||
RUN mkdir /tmp/qalculate
|
|
||||||
WORKDIR /tmp/qalculate
|
|
||||||
RUN install_packages ca-certificates wget xz-utils
|
|
||||||
RUN wget https://github.com/Qalculate/qalculate-gtk/releases/download/v${QALCULATE_VERSION}/qalculate-${QALCULATE_VERSION}-x86_64.tar.xz -O qalculate.tar.xz
|
|
||||||
RUN tar xf qalculate.tar.xz
|
|
||||||
RUN cp qalculate-${QALCULATE_VERSION}/* /tmp/qalculate
|
|
||||||
|
|
||||||
FROM bitnami/minideb:${DEBIAN_RELEASE}
|
|
||||||
RUN apt update
|
|
||||||
RUN apt install openssl libopus0 ffmpeg python3 python3-pip libpq5 pkg-config -y
|
|
||||||
COPY --from=qalculate-builder /tmp/qalculate/* /usr/bin/
|
|
||||||
COPY --from=builder /tmp/tobi/tobi-rs .
|
|
||||||
RUN pip3 install youtube-dl
|
|
||||||
RUN rm -rf /var/lib/{apt,dpkg,cache,log}/ /var/cache
|
|
||||||
ENTRYPOINT ["/tobi-rs"]
|
|
@ -1,5 +0,0 @@
|
|||||||
# For documentation on how to configure this file,
|
|
||||||
# see diesel.rs/guides/configuring-diesel-cli
|
|
||||||
|
|
||||||
[print_schema]
|
|
||||||
file = "src/schema.rs"
|
|
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "migration"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "migration"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
sea-orm-migration = "0.9.3"
|
||||||
|
tokio = { version = "1.21.2", features = ["rt", "net", "tracing"] }
|
@ -0,0 +1,37 @@
|
|||||||
|
# Running Migrator CLI
|
||||||
|
|
||||||
|
- Apply all pending migrations
|
||||||
|
```sh
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
```sh
|
||||||
|
cargo run -- up
|
||||||
|
```
|
||||||
|
- Apply first 10 pending migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- up -n 10
|
||||||
|
```
|
||||||
|
- Rollback last applied migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- down
|
||||||
|
```
|
||||||
|
- Rollback last 10 applied migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- down -n 10
|
||||||
|
```
|
||||||
|
- Drop all tables from the database, then reapply all migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- fresh
|
||||||
|
```
|
||||||
|
- Rollback all applied migrations, then reapply all migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- refresh
|
||||||
|
```
|
||||||
|
- Rollback all applied migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- reset
|
||||||
|
```
|
||||||
|
- Check the status of all migrations
|
||||||
|
```sh
|
||||||
|
cargo run -- status
|
||||||
|
```
|
@ -0,0 +1,16 @@
|
|||||||
|
pub use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
mod m20220029_164527_change_timestamp_format;
|
||||||
|
mod m20220101_000001_create_table;
|
||||||
|
|
||||||
|
pub struct Migrator;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigratorTrait for Migrator {
|
||||||
|
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||||
|
vec![
|
||||||
|
Box::new(m20220101_000001_create_table::Migration),
|
||||||
|
Box::new(m20220029_164527_change_timestamp_format::Migration),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
use crate::{DbErr, Table};
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum Statistics {
|
||||||
|
Table,
|
||||||
|
ExecutedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum EphemeralMessages {
|
||||||
|
Table,
|
||||||
|
Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
impl MigrationName for Migration {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"m20220029_164527_change_timestamp_format"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Statistics::Table)
|
||||||
|
.modify_column(
|
||||||
|
ColumnDef::new(Statistics::ExecutedAt)
|
||||||
|
.timestamp_with_time_zone()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(EphemeralMessages::Table)
|
||||||
|
.modify_column(
|
||||||
|
ColumnDef::new(EphemeralMessages::Timeout)
|
||||||
|
.timestamp_with_time_zone()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(Statistics::Table)
|
||||||
|
.modify_column(
|
||||||
|
ColumnDef::new(Statistics::ExecutedAt)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.alter_table(
|
||||||
|
Table::alter()
|
||||||
|
.table(EphemeralMessages::Table)
|
||||||
|
.modify_column(
|
||||||
|
ColumnDef::new(EphemeralMessages::Timeout)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,287 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum EphemeralMessages {
|
||||||
|
Table,
|
||||||
|
ChannelId,
|
||||||
|
MessageId,
|
||||||
|
Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum GuildPlaylists {
|
||||||
|
Table,
|
||||||
|
GuildId,
|
||||||
|
Name,
|
||||||
|
Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum GuildSettings {
|
||||||
|
Table,
|
||||||
|
GuildId,
|
||||||
|
Key,
|
||||||
|
Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum Media {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Category,
|
||||||
|
Name,
|
||||||
|
Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum Statistics {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Version,
|
||||||
|
Command,
|
||||||
|
ExecutedAt,
|
||||||
|
Success,
|
||||||
|
ErrorMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum YoutubeSongs {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
SpotifyId,
|
||||||
|
Artist,
|
||||||
|
Title,
|
||||||
|
Album,
|
||||||
|
Url,
|
||||||
|
Score,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MigrationName for Migration {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"m20220101_000001_create_table"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager.create_table(ephemeral_messages()).await?;
|
||||||
|
manager.create_table(guild_playlists()).await?;
|
||||||
|
manager.create_table(guild_settings()).await?;
|
||||||
|
manager.create_table(media()).await?;
|
||||||
|
manager.create_table(statistics()).await?;
|
||||||
|
manager.create_table(youtube_songs()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(EphemeralMessages::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(GuildPlaylists::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(GuildSettings::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Media::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Statistics::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(YoutubeSongs::Table).to_owned())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ephemeral_messages() -> TableCreateStatement {
|
||||||
|
Table::create()
|
||||||
|
.table(EphemeralMessages::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(EphemeralMessages::ChannelId)
|
||||||
|
.big_integer()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(EphemeralMessages::MessageId)
|
||||||
|
.big_integer()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(EphemeralMessages::Timeout)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.primary_key(
|
||||||
|
Index::create()
|
||||||
|
.col(EphemeralMessages::ChannelId)
|
||||||
|
.col(EphemeralMessages::MessageId),
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guild_playlists() -> TableCreateStatement {
|
||||||
|
Table::create()
|
||||||
|
.table(GuildPlaylists::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GuildPlaylists::GuildId)
|
||||||
|
.big_integer()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GuildPlaylists::Name)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GuildPlaylists::Url)
|
||||||
|
.string_len(1204)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.primary_key(
|
||||||
|
Index::create()
|
||||||
|
.col(GuildPlaylists::GuildId)
|
||||||
|
.col(GuildPlaylists::Name),
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guild_settings() -> TableCreateStatement {
|
||||||
|
Table::create()
|
||||||
|
.table(GuildSettings::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GuildSettings::GuildId)
|
||||||
|
.big_integer()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GuildSettings::Key)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(GuildSettings::Value)
|
||||||
|
.string_len(1024)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.primary_key(
|
||||||
|
Index::create()
|
||||||
|
.col(GuildSettings::GuildId)
|
||||||
|
.col(GuildSettings::Key),
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn media() -> TableCreateStatement {
|
||||||
|
Table::create()
|
||||||
|
.table(Media::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Media::Id)
|
||||||
|
.big_integer()
|
||||||
|
.auto_increment()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Media::Category).string_len(128))
|
||||||
|
.col(ColumnDef::new(Media::Name).string_len(128))
|
||||||
|
.col(ColumnDef::new(Media::Url).string_len(128))
|
||||||
|
.index(
|
||||||
|
Index::create()
|
||||||
|
.unique()
|
||||||
|
.col(Media::Category)
|
||||||
|
.col(Media::Name),
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statistics() -> TableCreateStatement {
|
||||||
|
Table::create()
|
||||||
|
.table(Statistics::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Statistics::Id)
|
||||||
|
.big_integer()
|
||||||
|
.auto_increment()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Statistics::Version)
|
||||||
|
.string_len(32)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Statistics::Command)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Statistics::ExecutedAt)
|
||||||
|
.timestamp()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Statistics::Success)
|
||||||
|
.boolean()
|
||||||
|
.not_null()
|
||||||
|
.default(true),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Statistics::ErrorMsg).string())
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn youtube_songs() -> TableCreateStatement {
|
||||||
|
Table::create()
|
||||||
|
.table(YoutubeSongs::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(YoutubeSongs::Id)
|
||||||
|
.big_integer()
|
||||||
|
.primary_key()
|
||||||
|
.auto_increment(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(YoutubeSongs::SpotifyId)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(YoutubeSongs::Artist)
|
||||||
|
.string_len(128)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(YoutubeSongs::Title)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(YoutubeSongs::Album)
|
||||||
|
.string_len(255)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(YoutubeSongs::Url).string_len(128).not_null())
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(YoutubeSongs::Score)
|
||||||
|
.integer()
|
||||||
|
.default(0)
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.index(
|
||||||
|
Index::create()
|
||||||
|
.unique()
|
||||||
|
.col(YoutubeSongs::SpotifyId)
|
||||||
|
.col(YoutubeSongs::Url),
|
||||||
|
)
|
||||||
|
.to_owned()
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
use migration::Migrator;
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
cli::run_cli(Migrator).await;
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
-- This file was automatically created by Diesel to setup helper functions
|
|
||||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
|
||||||
-- changes will be added to existing projects as new migrations.
|
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
|
||||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
|
@ -1,36 +0,0 @@
|
|||||||
-- This file was automatically created by Diesel to setup helper functions
|
|
||||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
|
||||||
-- changes will be added to existing projects as new migrations.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Sets up a trigger for the given table to automatically set a column called
|
|
||||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
|
||||||
-- in the modified columns)
|
|
||||||
--
|
|
||||||
-- # Example
|
|
||||||
--
|
|
||||||
-- ```sql
|
|
||||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
|
||||||
--
|
|
||||||
-- SELECT diesel_manage_updated_at('users');
|
|
||||||
-- ```
|
|
||||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
|
||||||
BEGIN
|
|
||||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
|
||||||
BEGIN
|
|
||||||
IF (
|
|
||||||
NEW IS DISTINCT FROM OLD AND
|
|
||||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
|
||||||
) THEN
|
|
||||||
NEW.updated_at := current_timestamp;
|
|
||||||
END IF;
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE guild_settings;
|
|
@ -1,6 +0,0 @@
|
|||||||
CREATE TABLE guild_settings (
|
|
||||||
guild_id BIGINT NOT NULL,
|
|
||||||
key VARCHAR(255) NOT NULL,
|
|
||||||
value VARCHAR(1024),
|
|
||||||
PRIMARY KEY (guild_id, key)
|
|
||||||
);
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE guild_playlists;
|
|
@ -1,7 +0,0 @@
|
|||||||
-- Your SQL goes here
|
|
||||||
CREATE TABLE guild_playlists (
|
|
||||||
guild_id BIGINT NOT NULL,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
url VARCHAR(1024) NOT NULL,
|
|
||||||
PRIMARY KEY (guild_id, name)
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE gifs;
|
|
@ -1,8 +0,0 @@
|
|||||||
-- Your SQL goes here
|
|
||||||
CREATE TABLE gifs (
|
|
||||||
id BIGSERIAL PRIMARY KEY ,
|
|
||||||
category VARCHAR(128),
|
|
||||||
name VARCHAR(128),
|
|
||||||
url VARCHAR(128) NOT NULL,
|
|
||||||
UNIQUE (category, name)
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE statistics;
|
|
@ -1,9 +0,0 @@
|
|||||||
-- Your SQL goes here
|
|
||||||
CREATE TABLE statistics (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
version VARCHAR(32) NOT NULL,
|
|
||||||
command VARCHAR(255) NOT NULL,
|
|
||||||
executed_at TIMESTAMP NOT NULL,
|
|
||||||
success BOOLEAN NOT NULL DEFAULT TRUE,
|
|
||||||
error_msg TEXT
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE youtube_songs;
|
|
@ -1,11 +0,0 @@
|
|||||||
-- Your SQL goes here
|
|
||||||
CREATE TABLE youtube_songs (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
spotify_id VARCHAR(255) NOT NULL,
|
|
||||||
artist VARCHAR(128) NOT NULL,
|
|
||||||
title VARCHAR(255) NOT NULL,
|
|
||||||
album VARCHAR(255) NOT NULL,
|
|
||||||
url VARCHAR(128) NOT NULL,
|
|
||||||
score INTEGER DEFAULT 0 NOT NULL,
|
|
||||||
UNIQUE (spotify_id, url)
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE ephemeral_messages;
|
|
@ -1,7 +0,0 @@
|
|||||||
-- Your SQL goes here
|
|
||||||
CREATE TABLE ephemeral_messages (
|
|
||||||
channel_id BIGINT NOT NULL,
|
|
||||||
message_id BIGINT NOT NULL,
|
|
||||||
timeout TIMESTAMP NOT NULL,
|
|
||||||
PRIMARY KEY (channel_id, message_id)
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
|
||||||
ALTER TABLE media RENAME TO gifs;
|
|
@ -1,2 +0,0 @@
|
|||||||
-- Your SQL goes here
|
|
||||||
ALTER TABLE gifs RENAME TO media;
|
|
@ -1,50 +1,49 @@
|
|||||||
|
use crate::entity::statistics;
|
||||||
|
use crate::error::DatabaseResult;
|
||||||
|
use sea_orm::prelude::*;
|
||||||
|
use sea_orm::ActiveValue::Set;
|
||||||
|
use sea_orm::{FromQueryResult, QuerySelect};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use diesel::dsl::count;
|
#[derive(FromQueryResult)]
|
||||||
use diesel::insert_into;
|
struct CommandCount {
|
||||||
use diesel::prelude::*;
|
count: i64,
|
||||||
use tokio_diesel::*;
|
}
|
||||||
|
|
||||||
use crate::error::DatabaseResult;
|
|
||||||
use crate::models::*;
|
|
||||||
use crate::schema::*;
|
|
||||||
use crate::Database;
|
|
||||||
|
|
||||||
impl Database {
|
impl super::BotDatabase {
|
||||||
/// Adds a command statistic to the database
|
/// Adds a command statistic to the database
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
pub async fn add_statistic(
|
pub async fn add_statistic(
|
||||||
&self,
|
&self,
|
||||||
version: &str,
|
version: String,
|
||||||
command: &str,
|
command: String,
|
||||||
executed_at: SystemTime,
|
executed_at: SystemTime,
|
||||||
success: bool,
|
success: bool,
|
||||||
error_msg: Option<String>,
|
error_msg: Option<String>,
|
||||||
) -> DatabaseResult<()> {
|
) -> DatabaseResult<()> {
|
||||||
use statistics::dsl;
|
let model = statistics::ActiveModel {
|
||||||
log::trace!("Adding statistic to database");
|
version: Set(version),
|
||||||
insert_into(dsl::statistics)
|
command: Set(command),
|
||||||
.values(StatisticInsert {
|
executed_at: Set(DateTimeLocal::from(executed_at).into()),
|
||||||
version: version.to_string(),
|
success: Set(success),
|
||||||
command: command.to_string(),
|
error_msg: Set(error_msg),
|
||||||
executed_at,
|
..Default::default()
|
||||||
success,
|
};
|
||||||
error_msg,
|
model.insert(&self.db).await?;
|
||||||
})
|
|
||||||
.execute_async(&self.pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total number of commands executed
|
/// Returns the total number of commands executed
|
||||||
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
pub async fn get_total_commands_statistic(&self) -> DatabaseResult<u64> {
|
pub async fn get_total_commands_statistic(&self) -> DatabaseResult<u64> {
|
||||||
use statistics::dsl;
|
let total_count: Option<CommandCount> = statistics::Entity::find()
|
||||||
log::trace!("Querying total number of commands");
|
.select_only()
|
||||||
let total_count: i64 = dsl::statistics
|
.column_as(statistics::Column::Id.count(), "count")
|
||||||
.select(count(dsl::id))
|
.into_model::<CommandCount>()
|
||||||
.first_async::<i64>(&self.pool)
|
.one(&self.db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(total_count as u64)
|
Ok(total_count.unwrap().count as u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "ephemeral_messages")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub channel_id: i64,
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub message_id: i64,
|
||||||
|
pub timeout: DateTimeWithTimeZone,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -0,0 +1,24 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "guild_playlists")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub guild_id: i64,
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub name: String,
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -0,0 +1,24 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "guild_settings")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub guild_id: i64,
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub key: String,
|
||||||
|
pub value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -0,0 +1,24 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "media")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i64,
|
||||||
|
pub category: Option<String>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -0,0 +1,10 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod ephemeral_messages;
|
||||||
|
pub mod guild_playlists;
|
||||||
|
pub mod guild_settings;
|
||||||
|
pub mod media;
|
||||||
|
pub mod statistics;
|
||||||
|
pub mod youtube_songs;
|
@ -0,0 +1,8 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
pub use super::ephemeral_messages::Entity as EphemeralMessages;
|
||||||
|
pub use super::guild_playlists::Entity as GuildPlaylists;
|
||||||
|
pub use super::guild_settings::Entity as GuildSettings;
|
||||||
|
pub use super::media::Entity as Media;
|
||||||
|
pub use super::statistics::Entity as Statistics;
|
||||||
|
pub use super::youtube_songs::Entity as YoutubeSongs;
|
@ -0,0 +1,27 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "statistics")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i64,
|
||||||
|
pub version: String,
|
||||||
|
pub command: String,
|
||||||
|
pub executed_at: DateTimeWithTimeZone,
|
||||||
|
pub success: bool,
|
||||||
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
|
pub error_msg: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -0,0 +1,27 @@
|
|||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.7.0
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "youtube_songs")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key)]
|
||||||
|
pub id: i64,
|
||||||
|
pub spotify_id: String,
|
||||||
|
pub artist: String,
|
||||||
|
pub title: String,
|
||||||
|
pub album: String,
|
||||||
|
pub url: String,
|
||||||
|
pub score: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!("No RelationDef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
@ -1,47 +1,31 @@
|
|||||||
#[macro_use]
|
use crate::error::DatabaseResult;
|
||||||
extern crate diesel;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate diesel_migrations;
|
|
||||||
|
|
||||||
use crate::error::{DatabaseError, DatabaseResult};
|
|
||||||
use diesel::prelude::*;
|
|
||||||
use diesel::r2d2::{ConnectionManager, ManageConnection, Pool};
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
pub mod database;
|
pub mod database;
|
||||||
|
pub mod entity;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod schema;
|
|
||||||
|
|
||||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub use database::Database;
|
pub use database::BotDatabase as Database;
|
||||||
|
use migration::MigratorTrait;
|
||||||
type PoolConnection = Pool<ConnectionManager<PgConnection>>;
|
use sea_orm::{ConnectOptions, Database as SeaDatabase, DatabaseConnection};
|
||||||
|
|
||||||
embed_migrations!("../bot-database/migrations");
|
#[tracing::instrument]
|
||||||
|
async fn get_connection() -> DatabaseResult<DatabaseConnection> {
|
||||||
fn get_connection() -> DatabaseResult<PoolConnection> {
|
|
||||||
let database_url = env::var("DATABASE_URL").expect("No DATABASE_URL in path");
|
let database_url = env::var("DATABASE_URL").expect("No DATABASE_URL in path");
|
||||||
log::debug!("Establishing database connection...");
|
tracing::debug!("Establishing database connection...");
|
||||||
let manager = ConnectionManager::<PgConnection>::new(database_url);
|
let opt = ConnectOptions::new(database_url);
|
||||||
log::trace!("Connecting...");
|
let db = SeaDatabase::connect(opt).await?;
|
||||||
manager
|
tracing::debug!("Running migrations...");
|
||||||
.connect()
|
migration::Migrator::up(&db, None).await?;
|
||||||
.map_err(|e| DatabaseError::Msg(format!("{:?}", e)))?;
|
tracing::debug!("Migrations finished");
|
||||||
log::trace!("Creating pool...");
|
tracing::info!("Database connection initialized");
|
||||||
let pool = Pool::builder().max_size(16).build(manager)?;
|
|
||||||
log::trace!("Getting one connection to run migrations...");
|
Ok(db)
|
||||||
let connection = pool.get()?;
|
|
||||||
log::debug!("Running migrations...");
|
|
||||||
embedded_migrations::run(&connection)?;
|
|
||||||
log::debug!("Migrations finished");
|
|
||||||
log::info!("Database connection initialized");
|
|
||||||
|
|
||||||
Ok(pool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_database() -> DatabaseResult<Database> {
|
pub async fn get_database() -> DatabaseResult<Database> {
|
||||||
let conn = get_connection()?;
|
let conn = get_connection().await?;
|
||||||
Ok(Database::new(conn))
|
Ok(Database::new(conn))
|
||||||
}
|
}
|
||||||
|
@ -1,94 +1,8 @@
|
|||||||
use crate::schema::*;
|
use super::entity;
|
||||||
use std::time::SystemTime;
|
|
||||||
|
pub use entity::ephemeral_messages::Model as EphemeralMessage;
|
||||||
#[derive(Queryable, Debug)]
|
pub use entity::guild_playlists::Model as GuildPlaylist;
|
||||||
pub struct GuildSetting {
|
pub use entity::guild_settings::Model as GuildSetting;
|
||||||
pub guild_id: i64,
|
pub use entity::media::Model as Media;
|
||||||
pub key: String,
|
pub use entity::statistics::Model as Statistic;
|
||||||
pub value: Option<String>,
|
pub use entity::youtube_songs::Model as YoutubeSong;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Debug)]
|
|
||||||
#[table_name = "guild_settings"]
|
|
||||||
pub struct GuildSettingInsert {
|
|
||||||
pub guild_id: i64,
|
|
||||||
pub key: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Debug)]
|
|
||||||
pub struct GuildPlaylist {
|
|
||||||
pub guild_id: i64,
|
|
||||||
pub name: String,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Debug)]
|
|
||||||
#[table_name = "guild_playlists"]
|
|
||||||
pub struct GuildPlaylistInsert {
|
|
||||||
pub guild_id: i64,
|
|
||||||
pub name: String,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Debug, Clone)]
|
|
||||||
pub struct Media {
|
|
||||||
pub id: i64,
|
|
||||||
pub category: Option<String>,
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Debug)]
|
|
||||||
#[table_name = "media"]
|
|
||||||
pub struct MediaInsert {
|
|
||||||
pub category: Option<String>,
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Debug)]
|
|
||||||
#[table_name = "statistics"]
|
|
||||||
pub struct StatisticInsert {
|
|
||||||
pub version: String,
|
|
||||||
pub command: String,
|
|
||||||
pub executed_at: SystemTime,
|
|
||||||
pub success: bool,
|
|
||||||
pub error_msg: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Debug, Clone)]
|
|
||||||
pub struct YoutubeSong {
|
|
||||||
pub id: i64,
|
|
||||||
pub spotify_id: String,
|
|
||||||
pub artist: String,
|
|
||||||
pub title: String,
|
|
||||||
pub album: String,
|
|
||||||
pub url: String,
|
|
||||||
pub score: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Debug)]
|
|
||||||
#[table_name = "youtube_songs"]
|
|
||||||
pub struct YoutubeSongInsert {
|
|
||||||
pub spotify_id: String,
|
|
||||||
pub artist: String,
|
|
||||||
pub title: String,
|
|
||||||
pub album: String,
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Queryable, Debug, Clone)]
|
|
||||||
pub struct EphemeralMessage {
|
|
||||||
pub channel_id: i64,
|
|
||||||
pub message_id: i64,
|
|
||||||
pub timeout: SystemTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable, Debug)]
|
|
||||||
#[table_name = "ephemeral_messages"]
|
|
||||||
pub struct EphemeralMessageInsert {
|
|
||||||
pub channel_id: i64,
|
|
||||||
pub message_id: i64,
|
|
||||||
pub timeout: SystemTime,
|
|
||||||
}
|
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
table! {
|
|
||||||
ephemeral_messages (channel_id, message_id) {
|
|
||||||
channel_id -> Int8,
|
|
||||||
message_id -> Int8,
|
|
||||||
timeout -> Timestamp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table! {
|
|
||||||
guild_playlists (guild_id, name) {
|
|
||||||
guild_id -> Int8,
|
|
||||||
name -> Varchar,
|
|
||||||
url -> Varchar,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table! {
|
|
||||||
guild_settings (guild_id, key) {
|
|
||||||
guild_id -> Int8,
|
|
||||||
key -> Varchar,
|
|
||||||
value -> Nullable<Varchar>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table! {
|
|
||||||
media (id) {
|
|
||||||
id -> Int8,
|
|
||||||
category -> Nullable<Varchar>,
|
|
||||||
name -> Nullable<Varchar>,
|
|
||||||
url -> Varchar,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table! {
|
|
||||||
statistics (id) {
|
|
||||||
id -> Int8,
|
|
||||||
version -> Varchar,
|
|
||||||
command -> Varchar,
|
|
||||||
executed_at -> Timestamp,
|
|
||||||
success -> Bool,
|
|
||||||
error_msg -> Nullable<Text>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table! {
|
|
||||||
youtube_songs (id) {
|
|
||||||
id -> Int8,
|
|
||||||
spotify_id -> Varchar,
|
|
||||||
artist -> Varchar,
|
|
||||||
title -> Varchar,
|
|
||||||
album -> Varchar,
|
|
||||||
url -> Varchar,
|
|
||||||
score -> Int4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
|
||||||
ephemeral_messages,
|
|
||||||
guild_playlists,
|
|
||||||
guild_settings,
|
|
||||||
media,
|
|
||||||
statistics,
|
|
||||||
youtube_songs,
|
|
||||||
);
|
|
@ -1,40 +0,0 @@
|
|||||||
use serenity::client::Context;
|
|
||||||
use serenity::framework::standard::macros::command;
|
|
||||||
use serenity::framework::standard::CommandResult;
|
|
||||||
use serenity::model::channel::Message;
|
|
||||||
|
|
||||||
use crate::commands::common::handle_autodelete;
|
|
||||||
use crate::providers::music::lavalink::Lavalink;
|
|
||||||
use crate::utils::initialize_lavalink;
|
|
||||||
use serenity_rich_interaction::core::SHORT_TIMEOUT;
|
|
||||||
use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
|
|
||||||
use std::mem;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[description("Resets the lavalink connection")]
|
|
||||||
#[aliases("reconnect_lavalink", "reset-lavalink", "reconnect-lavalink")]
|
|
||||||
#[num_args(0)]
|
|
||||||
#[owners_only]
|
|
||||||
async fn reset_lavalink(ctx: &Context, msg: &Message) -> CommandResult {
|
|
||||||
let app_info = ctx.http.get_current_application_info().await?;
|
|
||||||
destroy_lavalink(ctx).await;
|
|
||||||
|
|
||||||
initialize_lavalink(Arc::clone(&ctx.data), app_info).await?;
|
|
||||||
|
|
||||||
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
|
|
||||||
m.content("Reconnected to lavalink")
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
handle_autodelete(ctx, msg).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn destroy_lavalink(ctx: &Context) {
|
|
||||||
let mut data = ctx.data.write().await;
|
|
||||||
{
|
|
||||||
let lava_client = data.remove::<Lavalink>().unwrap();
|
|
||||||
mem::drop(lava_client);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use serenity::framework::standard::macros::command;
|
|
||||||
use serenity::framework::standard::{Args, CommandError, CommandResult};
|
|
||||||
use serenity::model::channel::Message;
|
|
||||||
use serenity::prelude::*;
|
|
||||||
|
|
||||||
use crate::commands::common::handle_autodelete;
|
|
||||||
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
|
|
||||||
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
|
|
||||||
use serenity_rich_interaction::core::{MEDIUM_TIMEOUT, SHORT_TIMEOUT};
|
|
||||||
use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[only_in(guilds)]
|
|
||||||
#[description("Loads an equalizer preset")]
|
|
||||||
#[usage("<preset>")]
|
|
||||||
#[num_args(1)]
|
|
||||||
#[example("bass")]
|
|
||||||
#[bucket("general")]
|
|
||||||
#[checks(DJ)]
|
|
||||||
async fn equalize(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
|
||||||
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
||||||
log::debug!("Changing equalizer for {}", guild.id);
|
|
||||||
let preset = args.single::<String>().unwrap();
|
|
||||||
|
|
||||||
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
|
|
||||||
player
|
|
||||||
} else {
|
|
||||||
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
|
|
||||||
.await
|
|
||||||
.map_err(CommandError::from);
|
|
||||||
};
|
|
||||||
|
|
||||||
let bands = match preset.to_lowercase().as_str() {
|
|
||||||
"metal" => lavalink_rs::EQ_METAL,
|
|
||||||
"boost" => lavalink_rs::EQ_BOOST,
|
|
||||||
"base" => lavalink_rs::EQ_BASE,
|
|
||||||
"piano" => lavalink_rs::EQ_PIANO,
|
|
||||||
_ => {
|
|
||||||
EphemeralMessage::create(&ctx.http, msg.channel_id, MEDIUM_TIMEOUT, |m| {
|
|
||||||
m.content(format!(
|
|
||||||
"Unknown preset '{}'. Available are 'metal', 'boost', 'base' and 'piano'",
|
|
||||||
preset
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
handle_autodelete(ctx, msg).await?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
{
|
|
||||||
let mut player = player.lock().await;
|
|
||||||
player.equalize_all(bands).await?;
|
|
||||||
}
|
|
||||||
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
|
|
||||||
m.content(format!("🎛️ Changed equalizer to '{}'", preset))
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
handle_autodelete(ctx, msg).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
use serenity::framework::standard::macros::command;
|
|
||||||
use serenity::framework::standard::{CommandError, CommandResult};
|
|
||||||
use serenity::model::channel::Message;
|
|
||||||
use serenity::prelude::*;
|
|
||||||
|
|
||||||
use crate::commands::common::handle_autodelete;
|
|
||||||
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
|
|
||||||
use crate::messages::music::equalizer::create_equalizer_message;
|
|
||||||
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
|
|
||||||
|
|
||||||
#[command]
|
|
||||||
#[only_in(guilds)]
|
|
||||||
#[description("Displays the equalizer for the music player")]
|
|
||||||
#[usage("")]
|
|
||||||
#[bucket("general")]
|
|
||||||
#[checks(DJ)]
|
|
||||||
async fn equalizer(ctx: &Context, msg: &Message) -> CommandResult {
|
|
||||||
let guild = msg.guild(&ctx.cache).await.unwrap();
|
|
||||||
log::debug!("Displaying equalizer for guild {}", guild.id);
|
|
||||||
|
|
||||||
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
|
|
||||||
player
|
|
||||||
} else {
|
|
||||||
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
|
|
||||||
.await
|
|
||||||
.map_err(CommandError::from);
|
|
||||||
};
|
|
||||||
|
|
||||||
create_equalizer_message(&ctx, msg.channel_id, player).await?;
|
|
||||||
handle_autodelete(ctx, msg).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
pub mod equalizer;
|
|
||||||
pub mod no_voicechannel;
|
pub mod no_voicechannel;
|
||||||
pub mod now_playing;
|
pub mod now_playing;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue