From 68e739c90eaae109df3ee39e691e9f16b964ff78 Mon Sep 17 00:00:00 2001 From: mii Date: Wed, 21 Dec 2022 00:46:52 +0000 Subject: [PATCH] init --- .dockerignore | 2 ++ .github/workflows/build.yml | 47 +++++++++++++++++++++++++ .gitignore | 4 +++ Cargo.toml | 26 ++++++++++++++ Dockerfile | 19 ++++++++++ LICENSE | 21 +++++++++++ docker-compose.yml | 9 +++++ manifest/ncb-ping.yaml | 28 +++++++++++++++ src/config.rs | 7 ++++ src/data.rs | 24 +++++++++++++ src/event_handler.rs | 23 ++++++++++++ src/events/message_receive.rs | 58 ++++++++++++++++++++++++++++++ src/events/mod.rs | 2 ++ src/events/ready.rs | 5 +++ src/main.rs | 66 +++++++++++++++++++++++++++++++++++ 15 files changed, 341 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 docker-compose.yml create mode 100644 manifest/ncb-ping.yaml create mode 100644 src/config.rs create mode 100644 src/data.rs create mode 100644 src/event_handler.rs create mode 100644 src/events/message_receive.rs create mode 100644 src/events/mod.rs create mode 100644 src/events/ready.rs create mode 100644 src/main.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b12e2ec --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +target +audio diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ba3c708 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,47 @@ +on: + push: + branches-ignore: + - '**' + tags: + - 'v*' +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + name: Checkout + - uses: docker/metadata-action@v3 + id: meta + with: + images: ghcr.io/morioka22/ncb-ping + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + - uses: docker/login-action@v1 + with: + registry: ghcr.io + username: morioka22 + password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max + - name: Move cache + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93dbe26 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Cargo.lock +/target +config.toml +*.swp diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ef6b982 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "ncb-ping" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde_json = "1.0" +serde = "1.0" +toml = "0.5" +async-trait = "0.1.57" +chrono = "0.4.23" + +[dependencies.uuid] +version = "0.8" +features = ["serde", "v4"] + +[dependencies.serenity] +version = "0.11.5" +features = ["builder", "cache", "client", "gateway", "model", "utils", "unstable_discord_api", "collector", "rustls_backend", "framework", "voice"] + + +[dependencies.tokio] +version = "1.0" +features = ["macros", "rt-multi-thread", "sync"] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e04560c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef +WORKDIR app + +FROM chef AS planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +RUN apt-get update && apt-get install -y --no-install-recommends libssl-dev pkg-config gcc && apt-get -y clean +RUN cargo chef cook --release --recipe-path recipe.json +COPY . . +RUN cargo build --release + +FROM debian:bullseye-slim AS runtime +WORKDIR /ncb-ping +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates libssl-dev && apt-get -y clean +COPY --from=builder /app/target/release/ncb-ping /usr/local/bin +ENTRYPOINT ["/usr/local/bin/ncb-ping"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5379c85 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 mii8080 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..341e785 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' + +services: + ncb-tts-r2: + container_name: ncb-tts-r2 + image: ghcr.io/morioka22/ncb-tts-r2:1.2 + environment: + - NCB_TOKEN=YOUR_BOT_TOKEN + - NCB_APP_ID=YOUR_BOT_ID diff --git a/manifest/ncb-ping.yaml b/manifest/ncb-ping.yaml new file mode 100644 index 0000000..5c51c04 --- /dev/null +++ b/manifest/ncb-ping.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ncb-ping-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: ncb-ping + template: + metadata: + labels: + app: ncb-ping + spec: + containers: + - name: ping + image: ghcr.io/morioka22/ncb-ping + env: + - name: NCB_TOKEN + valueFrom: + secretKeyRef: + name: ncb-secret + key: BOT_TOKEN + - name: NCB_APP_ID + valueFrom: + secretKeyRef: + name: ncb-secret + key: APP_ID diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..842a2f6 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,7 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Config { + pub token: String, + pub application_id: u64, +} diff --git a/src/data.rs b/src/data.rs new file mode 100644 index 0000000..5e2c6e4 --- /dev/null +++ b/src/data.rs @@ -0,0 +1,24 @@ +use std::{collections::HashMap, sync::Arc}; + +use chrono::{DateTime, Utc}; +use serenity::{ + futures::lock::Mutex, + model::prelude::{ChannelId, Message, UserId}, + prelude::TypeMapKey, +}; + +#[derive(Debug, Clone)] +pub struct Ping { + pub channel: ChannelId, + pub user_id: UserId, + pub author: UserId, + pub message: Message, + pub time: DateTime, + pub args: Vec, +} + +pub struct PingData; + +impl TypeMapKey for PingData { + type Value = Arc>>; +} diff --git a/src/event_handler.rs b/src/event_handler.rs new file mode 100644 index 0000000..6365eb8 --- /dev/null +++ b/src/event_handler.rs @@ -0,0 +1,23 @@ +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::{ + channel::Message, + gateway::Ready, + }, +}; + +use crate::events; + +pub struct Handler; + +#[async_trait] +impl EventHandler for Handler { + async fn message(&self, ctx: Context, message: Message) { + events::message_receive::message(ctx, message).await; + } + + async fn ready(&self, ctx: Context, ready: Ready) { + events::ready::ready(ctx, ready).await + } +} diff --git a/src/events/message_receive.rs b/src/events/message_receive.rs new file mode 100644 index 0000000..dbc807c --- /dev/null +++ b/src/events/message_receive.rs @@ -0,0 +1,58 @@ +use chrono::Utc; +use serenity::{model::prelude::Message, prelude::Context}; + +use crate::data::{PingData, Ping}; + +pub async fn message(ctx: Context, message: Message) { + if message.author.bot { + return; + } + + let guild_id = message.guild(&ctx.cache); + + if let None = guild_id { + return; + } + + let storage_lock = { + let data_read = ctx.data.read().await; + data_read + .get::() + .expect("Cannot get PingData") + .clone() + }; + + if message.mentions.len() == 1 { + let user = message.mentions.first().unwrap(); + let m = format!("PING {} ({}) 56(84) bytes of data.", user.name, user.id.0); + let ping_message = message.reply(&ctx.http, m).await.unwrap(); + + let ping = Ping { + channel: message.channel_id, + user_id: user.id, + author: message.author.id, + time: Utc::now(), + message: ping_message, + args: vec![] + }; + + let mut storage = storage_lock.lock().await; + storage.insert(user.id, ping.clone()); + } + + { + let mut storage = storage_lock.lock().await; + if !storage.contains_key(&message.author.id) { + return; + } + let ping = storage.get_mut(&message.author.id).unwrap(); + + if ping.channel == message.channel_id { + let user = ping.user_id.to_user(&ctx.http).await.unwrap(); + let time = Utc::now() - ping.time; + ping.message.edit(&ctx.http, |f| f.content(format!("--- {} ping statistics ---\n1 packets transmitted, 1 received, 0% packet loss, time {}ms", user.name, time.num_milliseconds()))).await.unwrap(); + message.channel_id.send_message(&ctx.http, |f| f.content(format!("<@{}>", ping.author.0))).await.unwrap(); + storage.remove(&message.author.id); + } + } +} diff --git a/src/events/mod.rs b/src/events/mod.rs new file mode 100644 index 0000000..596ab80 --- /dev/null +++ b/src/events/mod.rs @@ -0,0 +1,2 @@ +pub mod message_receive; +pub mod ready; diff --git a/src/events/ready.rs b/src/events/ready.rs new file mode 100644 index 0000000..354d628 --- /dev/null +++ b/src/events/ready.rs @@ -0,0 +1,5 @@ +use serenity::{model::prelude::Ready, prelude::Context}; + +pub async fn ready(_: Context, ready: Ready) { + println!("{} is connected!", ready.user.name); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..73520fd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,66 @@ +mod config; +mod data; +mod event_handler; +mod events; + +use std::{collections::HashMap, env, sync::Arc}; + +use config::Config; +use data::PingData; +use event_handler::Handler; +use serenity::{ + client::Client, framework::StandardFramework, futures::lock::Mutex, prelude::GatewayIntents, +}; + +/// Create discord client +/// +/// Example: +/// ```rust +/// let client = create_client("!", "BOT_TOKEN", 123456789123456789).await; +/// +/// client.start().await; +/// ``` +async fn create_client(prefix: &str, token: &str, id: u64) -> Result { + let framework = StandardFramework::new().configure(|c| c.with_whitespace(true).prefix(prefix)); + + Client::builder(token, GatewayIntents::all()) + .event_handler(Handler) + .application_id(id) + .framework(framework) + .await +} + +#[tokio::main] +async fn main() { + // Load config + let config = { + let config = std::fs::read_to_string("./config.toml"); + if let Ok(config) = config { + toml::from_str::(&config).expect("Cannot load config file.") + } else { + let token = env::var("NCB_TOKEN").unwrap(); + let application_id = env::var("NCB_APP_ID").unwrap(); + + Config { + token, + application_id: u64::from_str_radix(&application_id, 10).unwrap(), + } + } + }; + + // Create discord client + let mut client = create_client("p.", &config.token, config.application_id) + .await + .expect("Err creating client"); + + // Create TTS storage + { + let mut data = client.data.write().await; + data.insert::(Arc::new(Mutex::new(HashMap::default()))); + } + + // Run client + if let Err(why) = client.start().await { + println!("Client error: {:?}", why); + } +}