Skip to content

Commit

Permalink
Merge pull request #1 from emallson/master
Browse files Browse the repository at this point in the history
Update to master
  • Loading branch information
anthonyronda authored Apr 28, 2020
2 parents 5372780 + 15e10b4 commit d55bf1b
Show file tree
Hide file tree
Showing 28 changed files with 1,725 additions and 764 deletions.
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Copyright (c) 2020 J David Smith, All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75 changes: 75 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Discord bot for [Arena Highlander][discord] to run
leagues and display results.

## Setting Up

This codebase uses [Docker Compose](https://docs.docker.com/compose/) as the
primary build system and can be run on any system with that installed:

docker-compose build

It is often helpful to be able to run things (like `cargo check` or `cargo
test`) outside of docker. This project is implemented in (mostly)
[Rust](https://rustup.rs), so installing that---while not required---is
encouraged.

### Environment Variables

The bot uses 3 environment variables during its run, two of which are optional:

- `DATABASE_URL` (required): The connection string for the (PostgreSQL) database
- `DISCORD_TOKEN`: The access token for the discord bot. See their API docs for details.
- `SENTRY_TOKEN`: The access token for [sentry](https://sentry.io), which is used for logging in production.

For development, you'll want to copy the [example `.env`
file](bot-rs/.env.example) to `bot-rs/.env`. The example value provided uses
the default login credentials for the database. These credentials are obviously
NOT SECURE and should be changed if you are running outside of a local
development machine.

If `DISCORD_TOKEN` is unset, the only thing you'll be able to do is work with
existing results via the site (which is accessible at `localhost:3000` by
default)

### Database Initialization

The base docker images should set up *almost* everything. After successfully building, you will need to run:

```bash
docker-compose up db -d # start the DB first so postgres has time to start
docker-compose run botrs diesel database setup --locked-schema # setup the DB
```

Changes to the schema should be committed to version control using `diesel migration` commands. For example:

```bash
diesel migration generate example # generation migration named "example"
# edit your migration
diesel migration run # run the migration
diesel migration redo # revert and re-run the migration as necessary to get it right
```

Some test data is included in `db-setup/test_data.sql`. If you have
`psql` installed, you can import it with:

```bash
psql $DATABASE_URL < db-setup/test_data.sql
```

### Running Tests

The full test suite requires the database to be running and the `DATABASE_URL` environment variable to be set.

```bash
docker-compose up db -d # start the DB in the background
export DATABASE_URL=.... # set the DB connection info
cargo test # run the tests
docker-compose run botrs cargo test # alternative to cargo test if you didn't install rust locally
```

## Deploying

Contact `emallson` via the [Arena Highlander Discord][discord] if you are
interested in deploying your own league bot.

[discord]: https://discord.gg/h2nEQHg
7 changes: 7 additions & 0 deletions bot-rs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# connects to the default credentials in the docker-compose setup.
# change this if you're actually putting this online
DATABASE_URL=postgres://user:arstarst@db/db
# token needed to connect to discord
#DISCORD_TOKEN=
# token needed to connect to sentry
#SENTRY_TOKEN=
4 changes: 4 additions & 0 deletions bot-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ sentry = { version = "0.18.0", features = ["with_log"]}
[[bin]]
name = "bot"
path = "src/main.rs"

[[bin]]
name = "dummy"
path = "dummy.rs"
11 changes: 11 additions & 0 deletions bot-rs/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM rust
RUN apt update && apt install libpq5
RUN cargo install diesel_cli --no-default-features --features postgres
WORKDIR /opt/bot
COPY dummy.rs .
COPY Cargo.toml .
COPY Cargo.lock .
RUN cargo build --release --bin dummy --locked
COPY . .
RUN cargo build --release --bin bot --locked
CMD cargo run --release --bin bot --locked
Binary file removed bot-rs/db-setup/AllPrintings.sqlite
Binary file not shown.
4 changes: 0 additions & 4 deletions bot-rs/docker/Dockerfile

This file was deleted.

12 changes: 8 additions & 4 deletions bot-rs/migrations/2020-04-24-205441_record_view/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
create view deck_records (id, match_wins, match_losses, game_wins, game_losses) as
(
with
deck_wins as (select decks.id as id, coalesce(sum(winner_wins), 0) as game_wins, count(matches.id) as match_wins from decks left join matches on decks.id = matches.winning_deck and matches.confirmed group by decks.id),
deck_losses as (select decks.id as id, coalesce(sum(winner_wins), 0) as game_losses, count(matches.id) as match_losses from decks left join matches on decks.id = matches.losing_deck and matches.confirmed group by decks.id)
deck_wins as (select decks.id as id, coalesce(sum(winner_wins), 0) as game_wins, coalesce(sum(loser_wins), 0) as game_losses, count(matches.id) as match_wins
from decks left join matches on decks.id = matches.winning_deck and matches.confirmed
group by decks.id),
deck_losses as (select decks.id as id, coalesce(sum(winner_wins), 0) as game_losses, coalesce(sum(loser_wins), 0) as game_wins, count(matches.id) as match_losses
from decks left join matches on decks.id = matches.losing_deck and matches.confirmed
group by decks.id)
select
deck_wins.id as id,
match_wins,
match_losses,
game_wins,
game_losses
deck_wins.game_wins + deck_losses.game_wins as game_wins,
deck_wins.game_losses + deck_losses.game_losses as game_losses
from deck_wins left join deck_losses on deck_wins.id = deck_losses.id
order by match_wins desc
);
83 changes: 83 additions & 0 deletions bot-rs/src/commands/admin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use chrono::{Utc};
use chrono_english::{parse_date_string, Dialect};
use serenity::framework::standard::{
macros::{command, group},
Args, CommandResult,
};
use serenity::model::channel::Message;
use serenity::prelude::*;

use crate::DbConn;
use crate::actions;

#[group]
#[prefix("admin")]
#[only_in(guilds)]
#[required_permissions("ADMINISTRATOR")]
#[commands(new_league, list_leagues, delete_league)]
pub(crate) struct LeagueControl;

#[command]
#[num_args(3)]
#[delimiters(", ")]
#[description("Define a new league. All times are in UTC")]
#[usage("<title>, <start-date>, <end-date>")]
#[example("!new-league April 2020, 1 April 2020, 1 May 2020")]
fn new_league(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
let data = ctx.data.read();
let conn = data.get::<DbConn>().unwrap();
let title = args.single::<String>()?;
let from = parse_date_string(args.single::<String>()?.as_str(), Utc::now(), Dialect::Us)
.expect("Unable to parse 'from' argument");
let to = parse_date_string(args.single::<String>()?.as_str(), Utc::now(), Dialect::Us)
.expect("Unable to parse 'from' argument");
let _league = actions::league::create_league(&*conn.lock().unwrap(), title, from, to)
.expect("Unable to create new league");

msg.channel_id
.say(&ctx.http, "League successfully created!")?;

Ok(())
}

#[command]
#[description("List all existing leagues (includes inactive leagues).")]
fn list_leagues(ctx: &mut Context, msg: &Message) -> CommandResult {
let data = ctx.data.read();
let conn = data.get::<DbConn>().unwrap();
let leagues =
actions::league::list_leagues(&*conn.lock().unwrap()).expect("Unable to list leagues");

let message = leagues
.into_iter()
.map(|l| {
format!(
"**{} League** (id: {}): {} to {}",
l.title, l.id, l.start_date, l.end_date
)
})
.collect::<Vec<_>>()
.join("\n");
msg.channel_id.send_message(&ctx.http, |m| {
m.embed(|e| e.title("All Leagues").description(message));
m
})?;
Ok(())
}

#[command]
#[description("Delete a league. All associated decks will exist, but be unassigned to any league.")]
#[num_args(1)]
#[usage("<id>")]
fn delete_league(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
let id = args.single::<i32>()?;
let data = ctx.data.read();
let conn = data.get::<DbConn>().unwrap();

let _count = actions::league::delete_league(&*conn.lock().unwrap(), id)
.expect("Unable to delete league");
let message = format!("Deleted league {}", id);
msg.channel_id.say(&ctx.http, &message)?;

Ok(())
}
Loading

0 comments on commit d55bf1b

Please sign in to comment.