feat: send category
All checks were successful
Build / Build (push) Successful in 2m0s
Build / docker (push) Successful in 1m23s

This commit is contained in:
파링 2026-01-06 11:58:55 +09:00
parent 18eba1d443
commit e739c443ac
Signed by: paring
SSH key fingerprint: SHA256:8uCHhCpn/gVOLEaTolmbub9kfM6XBxWkIWmHxUZoWWk
7 changed files with 53 additions and 13 deletions

View file

@ -46,6 +46,7 @@ impl Handler {
: :
- `reactions`: map<string, i64> ( - ) - `reactions`: map<string, i64> ( - )
- `channel`: string ( ID) - `channel`: string ( ID)
- `category`: string ( ID, UNIT)
`result("채널ID", , "메시지에 사용될 아이콘(이모지 권장)")` (unit이나 ) `result("채널ID", , "메시지에 사용될 아이콘(이모지 권장)")` (unit이나 )

View file

@ -3,6 +3,7 @@ use serde::Deserialize;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Config { pub struct Config {
pub debug: Option<bool>,
pub bot: BotConfig, pub bot: BotConfig,
pub db: DatabaseConfig, pub db: DatabaseConfig,
} }

View file

@ -2,14 +2,14 @@ use sqlx::prelude::FromRow;
#[derive(Debug, FromRow)] #[derive(Debug, FromRow)]
pub struct GuildRow { pub struct GuildRow {
pub id: String, // pub id: String,
pub script: Option<String>, pub script: Option<String>,
} }
#[derive(Debug, FromRow)] #[derive(Debug, FromRow)]
pub struct MessageRow { pub struct MessageRow {
// pub guild_id: String, // pub guild_id: String,
pub message_id: String, // pub message_id: String,
pub counter_id: String, pub counter_id: String,
pub counter_channel_id: String, pub counter_channel_id: String,
} }

View file

@ -7,8 +7,8 @@ use serenity::{
Attachment, CacheHttp, ChannelId, Component, Context, CreateComponent, CreateMediaGallery, Attachment, CacheHttp, ChannelId, Component, Context, CreateComponent, CreateMediaGallery,
CreateMediaGalleryItem, CreateSeparator, CreateTextDisplay, CreateUnfurledMediaItem, CreateMediaGalleryItem, CreateSeparator, CreateTextDisplay, CreateUnfurledMediaItem,
CreateWebhook, EditWebhookMessage, EventHandler, ExecuteWebhook, FullEvent, CreateWebhook, EditWebhookMessage, EventHandler, ExecuteWebhook, FullEvent,
GenericChannelId, GuildId, Interaction, InteractionType, Message, MessageFlags, MessageId, GenericChannelId, GuildId, Interaction, Message, MessageFlags, MessageId, MessageSnapshot,
MessageSnapshot, Reaction, StickerFormatType, StickerItem, Webhook, WebhookId, Reaction, StickerFormatType, StickerItem, Webhook, WebhookId,
}, },
async_trait, async_trait,
futures::lock::Mutex, futures::lock::Mutex,
@ -151,12 +151,28 @@ impl Handler {
CloneBuilderMessage::from(msg.clone()) CloneBuilderMessage::from(msg.clone())
}; };
let Some(result) = let channel = reaction.channel(&ctx.http).await?;
paringboard::script::check(script, reactions, reaction.channel_id.to_string())?
debug!("channel: {channel:?}");
let Some(result) = paringboard::script::check(
script,
reactions,
reaction.channel_id.to_string(),
channel
.guild()
.and_then(|x| x.parent_id)
.map(|x| x.to_string()),
)
.inspect_err(|res| {
error!("check failed: {res:?}");
})?
else { else {
return Ok(()); return Ok(());
}; };
debug!("check result: {result:?}");
let channel_id = result let channel_id = result
.channel_id .channel_id
.parse() .parse()

View file

@ -1,5 +1,6 @@
use std::{str::FromStr, sync::Arc}; use std::{str::FromStr, sync::Arc};
use crate::{config::Config, handler::Handler};
use anyhow::Context; use anyhow::Context;
use dashmap::DashMap; use dashmap::DashMap;
use figment::{ use figment::{
@ -12,8 +13,7 @@ use serenity::{
all::{GatewayIntents, Token}, all::{GatewayIntents, Token},
}; };
use sqlx::{migrate, postgres::PgPoolOptions}; use sqlx::{migrate, postgres::PgPoolOptions};
use tracing::Level;
use crate::{config::Config, handler::Handler};
mod commands; mod commands;
mod config; mod config;
@ -26,7 +26,7 @@ extern crate tracing;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init(); let mut fmt = tracing_subscriber::fmt();
let figment = Figment::new() let figment = Figment::new()
.merge(Toml::file("config.toml")) .merge(Toml::file("config.toml"))
@ -34,6 +34,12 @@ async fn main() -> anyhow::Result<()> {
let config: Config = figment.extract()?; let config: Config = figment.extract()?;
if config.debug.unwrap_or(false) {
fmt = fmt.with_max_level(Level::DEBUG);
}
fmt.init();
info!("config: {config:?}"); info!("config: {config:?}");
let db = PgPoolOptions::new() let db = PgPoolOptions::new()
@ -49,7 +55,9 @@ async fn main() -> anyhow::Result<()> {
let mut client = Client::builder( let mut client = Client::builder(
Token::from_str(config.bot.token.expose_secret()).unwrap(), Token::from_str(config.bot.token.expose_secret()).unwrap(),
GatewayIntents::GUILD_MESSAGES | GatewayIntents::GUILD_MESSAGE_REACTIONS, GatewayIntents::GUILD_MESSAGES
| GatewayIntents::GUILD_MESSAGE_REACTIONS
| GatewayIntents::GUILDS,
) )
.event_handler(Arc::new(Handler { .event_handler(Arc::new(Handler {
db, db,

View file

@ -1,10 +1,10 @@
use anyhow::Context as _; use anyhow::Context as _;
use serenity::all::{ use serenity::all::{
Component, Context, CreateInteractionResponseMessage, Label, LabelComponent, ModalInteraction, Component, Context, CreateInteractionResponseMessage, LabelComponent, ModalInteraction,
Permissions, Permissions,
}; };
use crate::{db, handler::Handler}; use crate::handler::Handler;
impl Handler { impl Handler {
pub async fn process_modal( pub async fn process_modal(
@ -54,8 +54,8 @@ impl Handler {
.context("unable to get value text from input")?; .context("unable to get value text from input")?;
sqlx::query("insert into guilds (id, script) values ($1, $2) on conflict (id) do update set script = excluded.script") sqlx::query("insert into guilds (id, script) values ($1, $2) on conflict (id) do update set script = excluded.script")
.bind(value.as_str())
.bind(guild_id.to_string()) .bind(guild_id.to_string())
.bind(value.as_str())
.execute(&self.db) .execute(&self.db)
.await?; .await?;

View file

@ -3,6 +3,7 @@ use std::collections::HashMap;
use anyhow::anyhow; use anyhow::anyhow;
use rhai::{Dynamic, Engine, Map, Scope}; use rhai::{Dynamic, Engine, Map, Scope};
use serde::Deserialize; use serde::Deserialize;
use tracing::debug;
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct ReactionResult { pub struct ReactionResult {
@ -15,7 +16,9 @@ pub fn check(
script: &str, script: &str,
reactions: HashMap<String, i64>, reactions: HashMap<String, i64>,
channel: String, channel: String,
category: Option<String>,
) -> anyhow::Result<Option<ReactionResult>> { ) -> anyhow::Result<Option<ReactionResult>> {
debug!("script: {script}");
let mut engine = Engine::new(); let mut engine = Engine::new();
engine.set_max_operations(1000); engine.set_max_operations(1000);
engine.register_type::<ReactionResult>(); engine.register_type::<ReactionResult>();
@ -45,10 +48,21 @@ pub fn check(
scope.set_value("reactions", Dynamic::from(emotes_input)); scope.set_value("reactions", Dynamic::from(emotes_input));
scope.set_value("channel", Dynamic::from(channel)); scope.set_value("channel", Dynamic::from(channel));
scope.set_value(
"category",
if let Some(value) = category {
Dynamic::from(value)
} else {
Dynamic::UNIT
},
);
let result: Dynamic = engine let result: Dynamic = engine
.eval_with_scope(&mut scope, script) .eval_with_scope(&mut scope, script)
.map_err(|e| anyhow!("{e:?}"))?; .map_err(|e| anyhow!("{e:?}"))?;
debug!("result: {result:?}");
let result: Option<ReactionResult> = if result.is_unit() { let result: Option<ReactionResult> = if result.is_unit() {
None None
} else { } else {