mirror of
https://github.com/Noratrieb/widetom.git
synced 2026-01-17 02:15:02 +01:00
Compare commits
No commits in common. "387f6ff587b66186332cf5cfba90c966bba0695c" and "33d1738799618d72fe2b86896f766cbfea58dc76" have entirely different histories.
387f6ff587
...
33d1738799
7 changed files with 507 additions and 803 deletions
1114
Cargo.lock
generated
1114
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -11,10 +11,9 @@ tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||||
uwuify = "^0.2"
|
uwuify = "^0.2"
|
||||||
async-trait = "0.1.50"
|
async-trait = "0.1.50"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
toml = "0.9.8"
|
toml = "0.8.8"
|
||||||
fancy-regex = "0.16.2"
|
fancy-regex = "0.13.0"
|
||||||
rand = "0.9.2"
|
rand = "0.8.3"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
|
||||||
|
|
||||||
[dependencies.serenity]
|
[dependencies.serenity]
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
|
|
||||||
30
Dockerfile
Normal file
30
Dockerfile
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
FROM rust as build
|
||||||
|
|
||||||
|
RUN rustup toolchain install nightly
|
||||||
|
RUN rustup default nightly
|
||||||
|
RUN rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install musl-tools -y
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
RUN mkdir src
|
||||||
|
RUN echo "fn main() {}" > src/main.rs
|
||||||
|
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
COPY src ./src
|
||||||
|
|
||||||
|
# now rebuild with the proper main
|
||||||
|
RUN touch src/main.rs
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
### RUN
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=build /app/target/x86_64-unknown-linux-musl/release/widertom widertom
|
||||||
|
|
||||||
|
CMD ["/app/widertom"]
|
||||||
4
run.sh
Normal file
4
run.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
docker run -d -v "$(pwd)/config.toml:/app/config.toml" -v "$(pwd)/bot_token:/app/bot_token" \
|
||||||
|
-e CONFIG_PATH=/app/config.toml -e BOT_TOKEN_PATH=/app/bot_token --name widertom widertom:1.0
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use rand::seq::IndexedRandom;
|
use lazy_static::lazy_static;
|
||||||
|
use rand::Rng;
|
||||||
use serenity::all::{CreateEmbed, CreateMessage};
|
use serenity::all::{CreateEmbed, CreateMessage};
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::{
|
use serenity::framework::standard::{
|
||||||
|
|
@ -11,8 +12,11 @@ use serenity::framework::standard::{
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
use serenity::model::id::UserId;
|
use serenity::model::id::UserId;
|
||||||
use serenity::utils::{content_safe, ContentSafeOptions};
|
use serenity::utils::{content_safe, ContentSafeOptions};
|
||||||
|
use toml::Value;
|
||||||
|
use uwuifier::uwuify_str_sse;
|
||||||
|
|
||||||
use crate::{Config, LastMessageInChannel};
|
use crate::general::{reply, CONFIG, CONFIG_ERR, REACTION_EMOTES};
|
||||||
|
use crate::LastMessageInChannel;
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(say, list)]
|
#[commands(say, list)]
|
||||||
|
|
@ -24,18 +28,21 @@ struct General;
|
||||||
#[description = "meme commands"]
|
#[description = "meme commands"]
|
||||||
struct Meme;
|
struct Meme;
|
||||||
|
|
||||||
|
#[group]
|
||||||
|
#[commands(shutdown)]
|
||||||
|
#[owners_only]
|
||||||
|
#[description = "bot admin commands"]
|
||||||
|
struct Admin;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[description("lists all the commands")]
|
#[description("lists all the commands")]
|
||||||
async fn list(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
async fn list(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
||||||
let data = ctx.data.read().await;
|
|
||||||
let config = data.get::<Config>().unwrap();
|
|
||||||
msg.channel_id
|
msg.channel_id
|
||||||
.send_message(
|
.send_message(
|
||||||
&ctx.http,
|
&ctx.http,
|
||||||
CreateMessage::new().embed(
|
CreateMessage::new().embed(
|
||||||
CreateEmbed::new().title("Widetom reaction emotes").fields(
|
CreateEmbed::new().title("Widetom reaction emotes").fields(
|
||||||
config
|
REACTION_EMOTES
|
||||||
.emotes
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|em| (em.0, format!("<:{}:{}>", em.0, em.1.get()), false)),
|
.map(|em| (em.0, format!("<:{}:{}>", em.0, em.1.get()), false)),
|
||||||
),
|
),
|
||||||
|
|
@ -58,7 +65,7 @@ async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
.clean_role(false)
|
.clean_role(false)
|
||||||
};
|
};
|
||||||
|
|
||||||
let content = content_safe(&ctx.cache, args.rest(), &settings, &[]);
|
let content = content_safe(&ctx.cache, &args.rest(), &settings, &[]);
|
||||||
msg.delete(&ctx.http).await?;
|
msg.delete(&ctx.http).await?;
|
||||||
msg.channel_id.say(&ctx.http, &content).await?;
|
msg.channel_id.say(&ctx.http, &content).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -68,7 +75,7 @@ async fn say(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
#[description("uwuifies the arguments, or the last message in the channel if no args are supplied")]
|
#[description("uwuifies the arguments, or the last message in the channel if no args are supplied")]
|
||||||
async fn uwuify(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
async fn uwuify(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
if let Some(parent) = &msg.referenced_message {
|
if let Some(parent) = &msg.referenced_message {
|
||||||
let uwu = uwuifier::uwuify_str_sse(&parent.content);
|
let uwu = uwuify_str_sse(&*parent.content);
|
||||||
msg.channel_id.say(&ctx.http, uwu).await?;
|
msg.channel_id.say(&ctx.http, uwu).await?;
|
||||||
} else if args.is_empty() {
|
} else if args.is_empty() {
|
||||||
let mut data = ctx.data.write().await;
|
let mut data = ctx.data.write().await;
|
||||||
|
|
@ -78,7 +85,7 @@ async fn uwuify(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
let old_message = map.get(&msg.channel_id);
|
let old_message = map.get(&msg.channel_id);
|
||||||
match old_message {
|
match old_message {
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
let uwu = uwuifier::uwuify_str_sse(s);
|
let uwu = uwuify_str_sse(s);
|
||||||
msg.channel_id.say(&ctx.http, uwu).await?;
|
msg.channel_id.say(&ctx.http, uwu).await?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -88,21 +95,36 @@ async fn uwuify(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let uwu = uwuifier::uwuify_str_sse(args.rest());
|
let uwu = uwuify_str_sse(args.rest());
|
||||||
msg.channel_id.say(&ctx.http, uwu).await?;
|
msg.channel_id.say(&ctx.http, uwu).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description("end tom")]
|
||||||
|
async fn shutdown(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
||||||
|
reply(
|
||||||
|
"<:tom:811324632082415626> bye <:tom:811324632082415626>",
|
||||||
|
&msg,
|
||||||
|
&ctx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[description("display a random answer from the xp support applications")]
|
#[description("display a random answer from the xp support applications")]
|
||||||
async fn xp(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
async fn xp(ctx: &Context, msg: &Message, _: Args) -> CommandResult {
|
||||||
let data = ctx.data.read().await;
|
lazy_static! {
|
||||||
let config = data.get::<Config>().unwrap();
|
static ref XP_RESPONSES: &'static Vec<Value> = CONFIG
|
||||||
|
.get("xp")
|
||||||
let Some(random_value) = config.xp.choose(&mut rand::rng()) else {
|
.expect(CONFIG_ERR)
|
||||||
return Ok(());
|
.as_array()
|
||||||
};
|
.expect(CONFIG_ERR);
|
||||||
|
}
|
||||||
|
let index = rand::thread_rng().gen_range(0..XP_RESPONSES.len());
|
||||||
|
let random_value = XP_RESPONSES[index].as_str().expect(CONFIG_ERR);
|
||||||
msg.channel_id.say(&ctx.http, random_value).await?;
|
msg.channel_id.say(&ctx.http, random_value).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,50 @@
|
||||||
use std::sync::LazyLock;
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::standard::macros::hook;
|
use serenity::framework::standard::macros::hook;
|
||||||
use serenity::model::channel::{Message, ReactionType};
|
use serenity::model::channel::{Message, ReactionType};
|
||||||
|
use serenity::model::id::EmojiId;
|
||||||
|
use toml::Value;
|
||||||
|
|
||||||
use crate::{Config, LastMessageInChannel};
|
use crate::LastMessageInChannel;
|
||||||
|
|
||||||
|
pub static CONFIG_ERR: &'static str = "Invalid config file";
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref CONFIG: Value = {
|
||||||
|
let config_path =
|
||||||
|
std::env::var("CONFIG_PATH").unwrap_or_else(|_| "config.toml".to_string());
|
||||||
|
let config = fs::read_to_string(config_path)
|
||||||
|
.expect("Config file not found. Add 'config.toml' to this directory");
|
||||||
|
config.parse::<Value>().expect(CONFIG_ERR)
|
||||||
|
};
|
||||||
|
pub static ref REACTION_EMOTES: HashMap<String, EmojiId> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
let emotes = CONFIG.get("emotes").expect(CONFIG_ERR);
|
||||||
|
|
||||||
|
for v in emotes.as_array().expect(CONFIG_ERR) {
|
||||||
|
let name = v[0].as_str().expect(CONFIG_ERR).to_string();
|
||||||
|
let id = EmojiId::new(v[1].as_integer().expect(CONFIG_ERR).clone() as u64);
|
||||||
|
m.insert(name, id);
|
||||||
|
}
|
||||||
|
m
|
||||||
|
};
|
||||||
|
static ref RESPONSES: HashMap<String, String> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
|
||||||
|
let emotes = CONFIG.get("responses").expect(CONFIG_ERR);
|
||||||
|
|
||||||
|
for v in emotes.as_array().expect(CONFIG_ERR) {
|
||||||
|
let trigger = v[0].as_str().expect(CONFIG_ERR).to_string();
|
||||||
|
let response = v[1].as_str().expect(CONFIG_ERR).to_string();
|
||||||
|
m.insert(trigger, response);
|
||||||
|
}
|
||||||
|
m
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[hook]
|
#[hook]
|
||||||
pub async fn normal_message(ctx: &Context, msg: &Message) {
|
pub async fn normal_message(ctx: &Context, msg: &Message) {
|
||||||
|
|
@ -13,12 +52,11 @@ pub async fn normal_message(ctx: &Context, msg: &Message) {
|
||||||
let map = data
|
let map = data
|
||||||
.get_mut::<LastMessageInChannel>()
|
.get_mut::<LastMessageInChannel>()
|
||||||
.expect("LastMessageInChannel not found");
|
.expect("LastMessageInChannel not found");
|
||||||
map.insert(msg.channel_id, msg.content.clone());
|
map.insert(msg.channel_id.clone(), msg.content.clone());
|
||||||
|
|
||||||
let config = data.get::<Config>().unwrap();
|
lazy_static! {
|
||||||
|
static ref TOM_REGEX: Regex = Regex::new(r"(?<=^|\D)(\d{6})(?=\D|$)").unwrap();
|
||||||
static TOM_REGEX: LazyLock<Regex> =
|
}
|
||||||
LazyLock::new(|| Regex::new(r"(?<=^|\D)(\d{6})(?=\D|$)").unwrap());
|
|
||||||
|
|
||||||
let is_nsfw = msg
|
let is_nsfw = msg
|
||||||
.channel_id
|
.channel_id
|
||||||
|
|
@ -33,17 +71,17 @@ pub async fn normal_message(ctx: &Context, msg: &Message) {
|
||||||
.as_str()
|
.as_str()
|
||||||
.parse::<u32>()
|
.parse::<u32>()
|
||||||
.expect("matched regex, so it is valid");
|
.expect("matched regex, so it is valid");
|
||||||
reply(&format!("<https://nhentai.net/g/{}/>", number), msg, ctx).await;
|
reply(&*format!("<https://nhentai.net/g/{}/>", number), &msg, &ctx).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (trigger, answer) in config.responses.iter() {
|
for (trigger, answer) in RESPONSES.iter() {
|
||||||
if msg.content.to_lowercase() == *trigger {
|
if msg.content.to_lowercase() == *trigger {
|
||||||
reply(answer, msg, ctx).await;
|
reply(answer, &msg, &ctx).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, id) in config.emotes.iter() {
|
for (name, id) in REACTION_EMOTES.iter() {
|
||||||
if msg.content.to_lowercase().contains(name) {
|
if msg.content.to_lowercase().contains(name) {
|
||||||
if let Err(why) = msg
|
if let Err(why) = msg
|
||||||
.react(
|
.react(
|
||||||
|
|
|
||||||
41
src/main.rs
41
src/main.rs
|
|
@ -5,42 +5,24 @@ use std::collections::{HashMap, HashSet};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use serenity::all::standard::Configuration;
|
use serenity::all::standard::Configuration;
|
||||||
use serenity::all::EmojiId;
|
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::framework::StandardFramework;
|
use serenity::framework::StandardFramework;
|
||||||
use serenity::http::Http;
|
use serenity::http::Http;
|
||||||
use serenity::model::id::{ChannelId, UserId};
|
use serenity::model::id::{ChannelId, UserId};
|
||||||
use serenity::{async_trait, model::gateway::Ready, prelude::*};
|
use serenity::{async_trait, model::gateway::Ready, prelude::*};
|
||||||
|
|
||||||
use crate::commands::{GENERAL_GROUP, MEME_GROUP, MY_HELP};
|
use crate::commands::{ADMIN_GROUP, GENERAL_GROUP, MEME_GROUP, MY_HELP};
|
||||||
use crate::general::normal_message;
|
use crate::general::normal_message;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod general;
|
mod general;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
pub struct LastMessageInChannel;
|
||||||
struct ConfigFile {
|
|
||||||
emotes: Vec<(String, EmojiId)>,
|
|
||||||
responses: Vec<(String, String)>,
|
|
||||||
xp: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
emotes: HashMap<String, EmojiId>,
|
|
||||||
responses: HashMap<String, String>,
|
|
||||||
xp: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LastMessageInChannel;
|
|
||||||
|
|
||||||
impl TypeMapKey for LastMessageInChannel {
|
impl TypeMapKey for LastMessageInChannel {
|
||||||
type Value = HashMap<ChannelId, String>;
|
type Value = HashMap<ChannelId, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeMapKey for Config {
|
|
||||||
type Value = Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Handler;
|
struct Handler;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|
@ -52,22 +34,11 @@ impl EventHandler for Handler {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let config_path = std::env::var("CONFIG_PATH").unwrap_or_else(|_| "config.toml".to_string());
|
|
||||||
let config = fs::read_to_string(config_path)
|
|
||||||
.expect("Config file not found. Add 'config.toml' to this directory");
|
|
||||||
let config = toml::from_str::<ConfigFile>(&config).expect("invalid config file");
|
|
||||||
|
|
||||||
let config = Config {
|
|
||||||
emotes: config.emotes.into_iter().collect(),
|
|
||||||
responses: config.responses.into_iter().collect(),
|
|
||||||
xp: config.xp,
|
|
||||||
};
|
|
||||||
|
|
||||||
let token_path = std::env::var("BOT_TOKEN_PATH").unwrap_or_else(|_| "bot_token".to_string());
|
let token_path = std::env::var("BOT_TOKEN_PATH").unwrap_or_else(|_| "bot_token".to_string());
|
||||||
let token = fs::read_to_string(token_path).expect("Expected bot token in file 'bot_token'");
|
let token = fs::read_to_string(token_path).expect("Expected bot token in file 'bot_token'");
|
||||||
let token = token.trim();
|
let token = token.trim();
|
||||||
|
|
||||||
let http = Http::new(token);
|
let http = Http::new(&token);
|
||||||
|
|
||||||
http.get_current_application_info()
|
http.get_current_application_info()
|
||||||
.await
|
.await
|
||||||
|
|
@ -87,7 +58,8 @@ async fn main() {
|
||||||
.normal_message(normal_message)
|
.normal_message(normal_message)
|
||||||
.help(&MY_HELP)
|
.help(&MY_HELP)
|
||||||
.group(&GENERAL_GROUP)
|
.group(&GENERAL_GROUP)
|
||||||
.group(&MEME_GROUP);
|
.group(&MEME_GROUP)
|
||||||
|
.group(&ADMIN_GROUP);
|
||||||
framework.configure(
|
framework.configure(
|
||||||
Configuration::new()
|
Configuration::new()
|
||||||
.with_whitespace(false)
|
.with_whitespace(false)
|
||||||
|
|
@ -99,7 +71,7 @@ async fn main() {
|
||||||
|
|
||||||
// We don't really need all intents, but this is a small bot so we don't care.
|
// We don't really need all intents, but this is a small bot so we don't care.
|
||||||
let intents = GatewayIntents::all();
|
let intents = GatewayIntents::all();
|
||||||
let mut client = Client::builder(token, intents)
|
let mut client = Client::builder(&token, intents)
|
||||||
.event_handler(Handler)
|
.event_handler(Handler)
|
||||||
.framework(framework)
|
.framework(framework)
|
||||||
.await
|
.await
|
||||||
|
|
@ -108,7 +80,6 @@ async fn main() {
|
||||||
{
|
{
|
||||||
let mut data = client.data.write().await;
|
let mut data = client.data.write().await;
|
||||||
data.insert::<LastMessageInChannel>(HashMap::default());
|
data.insert::<LastMessageInChannel>(HashMap::default());
|
||||||
data.insert::<Config>(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(why) = client.start().await {
|
if let Err(why) = client.start().await {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue