initial commit
This commit is contained in:
commit
27ac44182d
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
.env
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.env
|
File diff suppressed because it is too large
Load Diff
|
@ -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"] }
|
|
@ -0,0 +1,5 @@
|
||||||
|
FROM rust:1.69-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
|
@ -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(())
|
||||||
|
}
|
|
@ -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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue