Some checks failed
BotServer CI / build (push) Failing after 1m34s
Split 20+ files over 1000 lines into focused subdirectories for better maintainability and code organization. All changes maintain backward compatibility through re-export wrappers. Major splits: - attendance/llm_assist.rs (2074→7 modules) - basic/keywords/face_api.rs → face_api/ (7 modules) - basic/keywords/file_operations.rs → file_ops/ (8 modules) - basic/keywords/hear_talk.rs → hearing/ (6 modules) - channels/wechat.rs → wechat/ (10 modules) - channels/youtube.rs → youtube/ (5 modules) - contacts/mod.rs → contacts_api/ (6 modules) - core/bootstrap/mod.rs → bootstrap/ (5 modules) - core/shared/admin.rs → admin_*.rs (5 modules) - designer/canvas.rs → canvas_api/ (6 modules) - designer/mod.rs → designer_api/ (6 modules) - docs/handlers.rs → handlers_api/ (11 modules) - drive/mod.rs → drive_handlers.rs, drive_types.rs - learn/mod.rs → types.rs - main.rs → main_module/ (7 modules) - meet/webinar.rs → webinar_api/ (8 modules) - paper/mod.rs → (10 modules) - security/auth.rs → auth_api/ (7 modules) - security/passkey.rs → (4 modules) - sources/mod.rs → sources_api/ (5 modules) - tasks/mod.rs → task_api/ (5 modules) Stats: 38,040 deletions, 1,315 additions across 318 files Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
350 lines
12 KiB
Rust
350 lines
12 KiB
Rust
use crate::channels::{ChannelManager, ChannelType, PostContent};
|
|
use crate::core::shared::models::UserSession;
|
|
use crate::core::shared::state::AppState;
|
|
use rhai::{Dynamic, Engine, EvalAltResult, Map};
|
|
use std::sync::Arc;
|
|
|
|
pub fn post_to_keyword(state: &Arc<AppState>, user: UserSession, engine: &mut Engine) {
|
|
let state_clone = Arc::clone(state);
|
|
let user_clone = user.clone();
|
|
|
|
engine.register_fn(
|
|
"POST_TO",
|
|
move |channel: &str, message: &str| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
post_to_impl(&state_clone, &user_clone, channel, message, None, None)
|
|
},
|
|
);
|
|
|
|
let state_clone = Arc::clone(state);
|
|
let user_clone = user.clone();
|
|
|
|
engine.register_fn(
|
|
"POST_TO_WITH_IMAGE",
|
|
move |channel: &str, message: &str, image: &str| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
post_to_impl(&state_clone, &user_clone, channel, message, Some(image), None)
|
|
},
|
|
);
|
|
|
|
let state_clone = Arc::clone(state);
|
|
let user_clone = user.clone();
|
|
|
|
engine.register_fn(
|
|
"POST_TO_WITH_VIDEO",
|
|
move |channel: &str, message: &str, video: &str| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
post_to_impl(&state_clone, &user_clone, channel, message, None, Some(video))
|
|
},
|
|
);
|
|
|
|
let state_clone = Arc::clone(state);
|
|
let user_clone = user.clone();
|
|
|
|
engine.register_fn(
|
|
"POST_TO_MULTIPLE",
|
|
move |channels: &str, message: &str| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
post_to_multiple_impl(&state_clone, &user_clone, channels, message, None, None)
|
|
},
|
|
);
|
|
|
|
let state_clone = Arc::clone(state);
|
|
let user_clone = user.clone();
|
|
|
|
engine.register_fn(
|
|
"POST_TO_ADVANCED",
|
|
move |options: Map| -> Result<Dynamic, Box<EvalAltResult>> {
|
|
post_to_advanced_impl(&state_clone, &user_clone, options)
|
|
},
|
|
);
|
|
}
|
|
|
|
fn post_to_impl(
|
|
state: &Arc<AppState>,
|
|
user: &UserSession,
|
|
channel: &str,
|
|
message: &str,
|
|
image: Option<&str>,
|
|
video: Option<&str>,
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
let channel_manager = get_channel_manager(state)?;
|
|
let account_name = channel.to_string();
|
|
|
|
let mut content = PostContent::text(message);
|
|
|
|
if let Some(img) = image {
|
|
content = content.with_image(img);
|
|
}
|
|
|
|
if let Some(vid) = video {
|
|
content = content.with_video(vid);
|
|
}
|
|
|
|
let rt = tokio::runtime::Handle::try_current().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("No async runtime available: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = rt.block_on(async { channel_manager.post_to(&account_name, &content).await });
|
|
|
|
match result {
|
|
Ok(post_result) => {
|
|
let mut map = Map::new();
|
|
map.insert("success".into(), Dynamic::from(post_result.success));
|
|
map.insert(
|
|
"channel".into(),
|
|
Dynamic::from(post_result.channel_type.to_string()),
|
|
);
|
|
if let Some(post_id) = post_result.post_id {
|
|
map.insert("post_id".into(), Dynamic::from(post_id));
|
|
}
|
|
if let Some(url) = post_result.url {
|
|
map.insert("url".into(), Dynamic::from(url));
|
|
}
|
|
if let Some(error) = post_result.error {
|
|
map.insert("error".into(), Dynamic::from(error));
|
|
}
|
|
Ok(Dynamic::from_map(map))
|
|
}
|
|
Err(e) => {
|
|
let mut map = Map::new();
|
|
map.insert("success".into(), Dynamic::from(false));
|
|
map.insert("error".into(), Dynamic::from(e.to_string()));
|
|
Ok(Dynamic::from_map(map))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn post_to_multiple_impl(
|
|
state: &Arc<AppState>,
|
|
user: &UserSession,
|
|
channels: &str,
|
|
message: &str,
|
|
image: Option<&str>,
|
|
video: Option<&str>,
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
let channel_manager = get_channel_manager(state)?;
|
|
|
|
let account_names: Vec<String> = channels
|
|
.split(',')
|
|
.map(|s| s.trim().to_string())
|
|
.filter(|s| !s.is_empty())
|
|
.collect();
|
|
|
|
let mut content = PostContent::text(message);
|
|
|
|
if let Some(img) = image {
|
|
content = content.with_image(img);
|
|
}
|
|
|
|
if let Some(vid) = video {
|
|
content = content.with_video(vid);
|
|
}
|
|
|
|
let rt = tokio::runtime::Handle::try_current().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("No async runtime available: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let results =
|
|
rt.block_on(async { channel_manager.post_to_multiple(&account_names, &content).await });
|
|
|
|
let mut total = 0;
|
|
let mut successful = 0;
|
|
let mut failed = 0;
|
|
let mut result_list = Vec::new();
|
|
|
|
for result in results {
|
|
total += 1;
|
|
match result {
|
|
Ok(post_result) => {
|
|
if post_result.success {
|
|
successful += 1;
|
|
} else {
|
|
failed += 1;
|
|
}
|
|
let mut item = Map::new();
|
|
item.insert("success".into(), Dynamic::from(post_result.success));
|
|
item.insert(
|
|
"channel".into(),
|
|
Dynamic::from(post_result.channel_type.to_string()),
|
|
);
|
|
if let Some(post_id) = post_result.post_id {
|
|
item.insert("post_id".into(), Dynamic::from(post_id));
|
|
}
|
|
if let Some(url) = post_result.url {
|
|
item.insert("url".into(), Dynamic::from(url));
|
|
}
|
|
if let Some(error) = post_result.error {
|
|
item.insert("error".into(), Dynamic::from(error));
|
|
}
|
|
result_list.push(Dynamic::from_map(item));
|
|
}
|
|
Err(e) => {
|
|
failed += 1;
|
|
let mut item = Map::new();
|
|
item.insert("success".into(), Dynamic::from(false));
|
|
item.insert("error".into(), Dynamic::from(e.to_string()));
|
|
result_list.push(Dynamic::from_map(item));
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut map = Map::new();
|
|
map.insert("total".into(), Dynamic::from(total as i64));
|
|
map.insert("successful".into(), Dynamic::from(successful as i64));
|
|
map.insert("failed".into(), Dynamic::from(failed as i64));
|
|
map.insert("results".into(), Dynamic::from(result_list));
|
|
|
|
Ok(Dynamic::from_map(map))
|
|
}
|
|
|
|
fn post_to_advanced_impl(
|
|
state: &Arc<AppState>,
|
|
user: &UserSession,
|
|
options: Map,
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
let channel_manager = get_channel_manager(state)?;
|
|
|
|
let channel = options
|
|
.get("channel")
|
|
.and_then(|v| v.clone().into_string().ok())
|
|
.ok_or_else(|| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
"Missing 'channel' in options".into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let message = options
|
|
.get("message")
|
|
.and_then(|v| v.clone().into_string().ok())
|
|
.unwrap_or_default();
|
|
|
|
let mut content = PostContent::text(message);
|
|
|
|
if let Some(image) = options.get("image").and_then(|v| v.clone().into_string().ok()) {
|
|
content = content.with_image(image);
|
|
}
|
|
|
|
if let Some(images) = options.get("images") {
|
|
if let Some(arr) = images.clone().try_cast::<rhai::Array>() {
|
|
for img in arr {
|
|
if let Ok(url) = img.into_string() {
|
|
content = content.with_image(url);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(video) = options.get("video").and_then(|v| v.clone().into_string().ok()) {
|
|
content = content.with_video(video);
|
|
}
|
|
|
|
if let Some(link) = options.get("link").and_then(|v| v.clone().into_string().ok()) {
|
|
content = content.with_link(link);
|
|
}
|
|
|
|
if let Some(hashtags) = options.get("hashtags") {
|
|
if let Some(arr) = hashtags.clone().try_cast::<rhai::Array>() {
|
|
let tags: Vec<String> = arr
|
|
.into_iter()
|
|
.filter_map(|v| v.into_string().ok())
|
|
.collect();
|
|
content = content.with_hashtags(tags);
|
|
}
|
|
}
|
|
|
|
if let Some(mentions) = options.get("mentions") {
|
|
if let Some(arr) = mentions.clone().try_cast::<rhai::Array>() {
|
|
let mention_list: Vec<String> = arr
|
|
.into_iter()
|
|
.filter_map(|v| v.into_string().ok())
|
|
.collect();
|
|
content = content.with_mentions(mention_list);
|
|
}
|
|
}
|
|
|
|
let rt = tokio::runtime::Handle::try_current().map_err(|e| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
format!("No async runtime available: {}", e).into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let result = rt.block_on(async { channel_manager.post_to(&channel, &content).await });
|
|
|
|
match result {
|
|
Ok(post_result) => {
|
|
let mut map = Map::new();
|
|
map.insert("success".into(), Dynamic::from(post_result.success));
|
|
map.insert(
|
|
"channel".into(),
|
|
Dynamic::from(post_result.channel_type.to_string()),
|
|
);
|
|
if let Some(post_id) = post_result.post_id {
|
|
map.insert("post_id".into(), Dynamic::from(post_id));
|
|
}
|
|
if let Some(url) = post_result.url {
|
|
map.insert("url".into(), Dynamic::from(url));
|
|
}
|
|
if let Some(error) = post_result.error {
|
|
map.insert("error".into(), Dynamic::from(error));
|
|
}
|
|
Ok(Dynamic::from_map(map))
|
|
}
|
|
Err(e) => {
|
|
let mut map = Map::new();
|
|
map.insert("success".into(), Dynamic::from(false));
|
|
map.insert("error".into(), Dynamic::from(e.to_string()));
|
|
Ok(Dynamic::from_map(map))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_channel_manager(state: &Arc<AppState>) -> Result<Arc<ChannelManager>, Box<EvalAltResult>> {
|
|
state.channel_manager.clone().ok_or_else(|| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
"Channel manager not configured".into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})
|
|
}
|
|
|
|
pub fn get_channel_limits(channel_name: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
let channel_type: ChannelType = channel_name.parse().map_err(|e: crate::channels::ChannelError| {
|
|
Box::new(EvalAltResult::ErrorRuntime(
|
|
e.to_string().into(),
|
|
rhai::Position::NONE,
|
|
))
|
|
})?;
|
|
|
|
let mut map = Map::new();
|
|
|
|
let (max_text, supports_images, supports_video, supports_links) = match channel_type {
|
|
ChannelType::Twitter => (280, true, true, true),
|
|
ChannelType::Bluesky => (300, true, false, true),
|
|
ChannelType::Threads => (500, true, true, true),
|
|
ChannelType::Instagram => (2200, true, true, true),
|
|
ChannelType::Facebook => (63206, true, true, true),
|
|
ChannelType::LinkedIn => (3000, true, true, true),
|
|
ChannelType::Discord => (2000, true, true, true),
|
|
ChannelType::Telegram => (4096, true, true, true),
|
|
ChannelType::WhatsApp => (4096, true, true, true),
|
|
ChannelType::Reddit => (40000, true, true, true),
|
|
ChannelType::TikTok => (2200, false, true, true),
|
|
ChannelType::YouTube => (5000, false, true, true),
|
|
ChannelType::Pinterest => (500, true, false, true),
|
|
ChannelType::Snapchat => (250, true, true, false),
|
|
ChannelType::WeChat => (2000, true, true, true),
|
|
ChannelType::TwilioSms => (1600, false, false, true),
|
|
};
|
|
|
|
map.insert("max_text_length".into(), Dynamic::from(max_text as i64));
|
|
map.insert("supports_images".into(), Dynamic::from(supports_images));
|
|
map.insert("supports_video".into(), Dynamic::from(supports_video));
|
|
map.insert("supports_links".into(), Dynamic::from(supports_links));
|
|
|
|
Ok(Dynamic::from_map(map))
|
|
}
|