This commit is contained in:
mii
2022-12-21 00:46:52 +00:00
commit 68e739c90e
15 changed files with 341 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
target
audio

47
.github/workflows/build.yml vendored Normal file
View File

@ -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

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
Cargo.lock
/target
config.toml
*.swp

26
Cargo.toml Normal file
View File

@ -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"]

19
Dockerfile Normal file
View File

@ -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"]

21
LICENSE Normal file
View File

@ -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.

9
docker-compose.yml Normal file
View File

@ -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

28
manifest/ncb-ping.yaml Normal file
View File

@ -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

7
src/config.rs Normal file
View File

@ -0,0 +1,7 @@
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Config {
pub token: String,
pub application_id: u64,
}

24
src/data.rs Normal file
View File

@ -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<Utc>,
pub args: Vec<String>,
}
pub struct PingData;
impl TypeMapKey for PingData {
type Value = Arc<Mutex<HashMap<UserId, Ping>>>;
}

23
src/event_handler.rs Normal file
View File

@ -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
}
}

View File

@ -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::<PingData>()
.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);
}
}
}

2
src/events/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod message_receive;
pub mod ready;

5
src/events/ready.rs Normal file
View File

@ -0,0 +1,5 @@
use serenity::{model::prelude::Ready, prelude::Context};
pub async fn ready(_: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);
}

66
src/main.rs Normal file
View File

@ -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<Client, serenity::Error> {
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>(&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::<PingData>(Arc::new(Mutex::new(HashMap::default())));
}
// Run client
if let Err(why) = client.start().await {
println!("Client error: {:?}", why);
}
}