initial commit

This commit is contained in:
DataHearth 2023-05-04 22:26:37 +02:00
commit 27ac44182d
No known key found for this signature in database
GPG Key ID: E88FD356ACC5F3C4
8 changed files with 2210 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
target
.env

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
.env

1980
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "tech-bot"
version = "0.1.0"
edition = "2021"
authors = ["Antoine Langlois <antoine.l@antoine-langlois.net>"]
description = "A Discord bot for the Tech channel"
[dependencies]
once_cell = "1.17.1"
poise = "0.5"
polodb_core = "4.2.0"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.28.0", features = ["macros", "rt-multi-thread"] }

5
Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM rust:1.69-alpine
WORKDIR /app
COPY . .

101
src/commands.rs Normal file
View File

@ -0,0 +1,101 @@
use std::sync::Mutex;
use once_cell::sync::OnceCell;
use poise::command;
use crate::database::Database;
const HELP_MESSAGE: &str = "
Hello fellow human! I am a bot that can help you adding new technologies to a git repository.
To add a new technology, just type:
```
/add <technology> <link>
```
To list all technologies, just type:
```
/list
```
To search for a technology, just type:
```
/search <technology>
```
To remove a technology, you need to have the permission to remote a tech from the list.
If so, just type:
```
/remove <technology>
```
To get help, just type:
```
/help
```
";
const AUTHORIZED_USERS: [u64; 1] = [252497456447750144];
type Error = Box<dyn std::error::Error + Send + Sync>;
type Context<'a> = poise::Context<'a, MsgData, Error>;
pub struct MsgData {}
pub static DB: OnceCell<Mutex<Database>> = OnceCell::new();
/// Show help for all commands
#[command(slash_command, prefix_command)]
pub async fn help(ctx: Context<'_>) -> Result<(), Error> {
ctx.say(HELP_MESSAGE).await?;
Ok(())
}
/// Add a new technology to the technologies list
#[command(slash_command, prefix_command)]
pub async fn add(
ctx: Context<'_>,
#[description = "Technology name"] technology: String,
#[description = "Git repository link"] link: String,
) -> Result<(), Error> {
ctx.say(format!("Added {technology} with link {link}"))
.await?;
Ok(())
}
/// List all available technologies
#[command(slash_command, prefix_command)]
pub async fn list(ctx: Context<'_>) -> Result<(), Error> {
ctx.say("Listed all technologies").await?;
Ok(())
}
/// Search for a technology
#[command(slash_command, prefix_command)]
pub async fn search(
ctx: Context<'_>,
#[description = "Technology name"] technology: String,
) -> Result<(), Error> {
ctx.say(format!("Found {technology}")).await?;
Ok(())
}
/// Remove a technology from the technologies list
#[command(slash_command, prefix_command)]
pub async fn remove(
ctx: Context<'_>,
#[description = "Technology name"] technology: String,
) -> Result<(), Error> {
if !AUTHORIZED_USERS.contains(&ctx.author().id.0) {
ctx.say("You don't have permission to remove a technology")
.await?;
return Ok(());
}
ctx.say(format!("Removed {technology}")).await?;
Ok(())
}

78
src/database.rs Normal file
View File

@ -0,0 +1,78 @@
use polodb_core::{
bson::{doc, oid::ObjectId},
Database as DB,
};
use serde::{Deserialize, Serialize};
use std::{
io::{Error, ErrorKind},
str::FromStr,
};
#[derive(Default, Debug, Serialize, Deserialize)]
struct Technology {
link: String,
name: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct AuthorizedUser {
name: String,
}
pub struct Database {
pub db: DB,
}
impl Database {
/// Create a new database instance.
pub fn new() -> Self {
Self {
db: DB::open_file("test-polo.db").expect("failed to initialize database"),
}
}
/// Add a new technology to the database.
pub fn add_tech(&self, link: String, name: String) -> Result<String, Error> {
Ok(self
.db
.collection::<Technology>("technologies")
.insert_one(Technology { link, name })
.map_err(|err| Error::new(ErrorKind::InvalidInput, err))?
.inserted_id
.to_string())
}
pub fn remove_tech(&self, id: &str) -> Result<(), Error> {
self.db
.collection::<Technology>("technologies")
.delete_one(doc! { "_id": Some(ObjectId::from_str(id).map_err(|err| Error::new(ErrorKind::InvalidInput, err))?) })
.map_err(|err| Error::new(ErrorKind::InvalidInput, err))?;
Ok(())
}
pub fn list_tech(&self) -> Result<Vec<Technology>, Error> {
Ok(self
.db
.collection("technologies")
.find(None)
.map_err(|err| Error::new(ErrorKind::InvalidInput, err))?
.filter(|doc| doc.is_ok())
.map(|doc| doc.unwrap())
.collect())
}
pub fn search_tech(&self, name: String) -> Result<Technology, Error> {
if let Some(tech) = self
.db
.collection::<Technology>("technologies")
.find(doc! { "$eq": [{"name": name}] })
.map_err(|err| Error::new(ErrorKind::InvalidInput, err))?
.next()
{
Ok(tech.map_err(|err| Error::new(ErrorKind::InvalidInput, err))?)
} else {
Err(Error::new(ErrorKind::NotFound, "Technology not found"))
}
}
}

29
src/main.rs Normal file
View File

@ -0,0 +1,29 @@
mod commands;
mod database;
use poise::serenity_prelude as serenity;
use commands::{add, help, list, remove, search, MsgData, DB};
#[tokio::main]
async fn main() {
DB.set(Mutex)
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions {
commands: vec![help(), add(), list(), search(), remove()],
..Default::default()
})
.token(std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN"))
.intents(
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT,
)
.setup(|ctx, _ready, framework| {
Box::pin(async move {
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
Ok(MsgData {})
})
});
framework.run().await.unwrap();
}