Skip to content

Commit

Permalink
0.1.0 -> 0.1.1 (#45)
Browse files Browse the repository at this point in the history
* 0.1.1 base

* additional fix for sort

* format folders

* fix connect issue
  • Loading branch information
tikitko authored Jan 15, 2023
1 parent 984e90f commit 69ea55f
Show file tree
Hide file tree
Showing 15 changed files with 376 additions and 306 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "red-alert"
version = "0.1.0"
version = "0.1.1"
edition = "2021"

[dependencies]
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Red Alert Main Configuration `config.yaml`:
```yaml
discord_token: "DISCORD_TOKEN"
lang_id: "ru_RU"
listening_text: "красную тревогу..."
vosk_model_path: "vosk-model-small-ru-0.22"
vosk_log_level: -1
```
Expand Down
3 changes: 2 additions & 1 deletion ru_RU.ftl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
listening-text = красную тревогу...
help-command-prefix-anchor = кринж киллер помощь
help-command-full-header = > **`{$header} {$suffix}`**
help-command-short-header = > **`{$header}`**
Expand Down Expand Up @@ -51,7 +52,7 @@ actions-history-red-alert-command-voice-record-reason-format = __{$reason}__
actions-history-red-alert-command-voice-self-record = КРИНЖОВИК {$target-name} {$status} ФРАЗОЙ "{$reason-text}" ГДЕ ЕСТЬ СОВПАДЕНИЕ С "{$restricted-word}" НА {$similarity-percent}%.
actions-history-red-alert-command-voice-target-record = КРИНЖОВИК {$target-name} {$status} ГОЛОСОМ МИРОТВОРЦA {$author-name} ПРИ ПОМОЩИ ФРАЗЫ "{$reason-text}" ГДЕ ЕСТЬ СОВПАДЕНИЕ С "{$restricted-word}" НА {$similarity-percent}%.
actions-history-red-alert-command-text-self-record = КРИНЖОВИК {$target-name} {$status} КОМАНДОЙ
actions-history-red-alert-command-text-target-record = КРИНЖОВИК {$target-name} {status} КОМАНДОЙ МИРОТВОРЦA {$author-name}
actions-history-red-alert-command-text-target-record = КРИНЖОВИК {$target-name} {$status} КОМАНДОЙ МИРОТВОРЦA {$author-name}
actions-history-red-alert-command-record = {$record-number}. [ВРЕМЯ: {$time}] {$record}.
actions-history-red-alert-command-empty-list = ПОКА ЕЩЕ НИКОГО НЕ УШАТАЛ НА ЭТОМ СЕРВЕР)!1!))
guilds-voice-config-red-alert-command-prefix-anchor = код красный настройка голоса
Expand Down
4 changes: 2 additions & 2 deletions src/components/discord_chat/commands_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ impl EventHandler for Handler {
.iter()
.collect::<Vec<&Box<dyn Command + Send + Sync + 'static>>>();
commands.push(&help_command);
let mut args_commands: Vec<(Vec<String>, &Box<dyn Command + Send + Sync + 'static>)> =
vec![];
let mut args_commands =
Vec::<(Vec<String>, &Box<dyn Command + Send + Sync + 'static>)>::new();
for command in commands {
args_commands.push((args(command.prefix_anchor()), command))
}
Expand Down
3 changes: 0 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ async fn main() {

let l10n = components::L10n::load(&lang_id_string);

let listening_text = settings.get_string("listening_text").ok();

let vosk_model_path = settings
.get_string("vosk_model_path")
.expect("Expected a VOSK model path in the config!");
Expand All @@ -59,7 +57,6 @@ async fn main() {
red_alert::RedAlertCommandsHandlerConstructor {
recognition_model: VoskModel::new(vosk_model_path.as_str())
.expect("Incorrect recognition model!"),
listening_text,
red_alert_handler: Arc::new(red_alert::RedAlertHandler),
l10n,
}
Expand Down
21 changes: 5 additions & 16 deletions src/red_alert/commands_handler/guilds_voice_config_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,7 @@ impl Command for GuildsVoiceConfigRedAlertCommand {
return;
};
let mut guilds_voice_config = self.guilds_voice_config.write().await;
let mut guild_voice_config = {
if let Some(specific) = guilds_voice_config.specific.remove(&guild_id.0) {
specific
} else {
let base = guilds_voice_config.base.clone();
base
}
};
let mut guild_voice_config = guilds_voice_config.remove(&guild_id);
let mut args = params.args.to_vec();
let answer_msg = {
let access_granted = guild_voice_config
Expand Down Expand Up @@ -359,16 +352,14 @@ impl Command for GuildsVoiceConfigRedAlertCommand {
fluent_args![],
) == action_string
{
if guilds_voice_config.auto_track_ids.contains(&guild_id.0) {
guilds_voice_config.auto_track_ids.remove(&guild_id.0);
if guilds_voice_config.switch_auto_track(guild_id) {
self.l10n.string(
"guilds-voice-config-red-alert-command-auto-track-remove",
"guilds-voice-config-red-alert-command-auto-track-add",
fluent_args![],
)
} else {
guilds_voice_config.auto_track_ids.insert(guild_id.0);
self.l10n.string(
"guilds-voice-config-red-alert-command-auto-track-add",
"guilds-voice-config-red-alert-command-auto-track-remove",
fluent_args![],
)
}
Expand All @@ -380,9 +371,7 @@ impl Command for GuildsVoiceConfigRedAlertCommand {
}
}
};
guilds_voice_config
.specific
.insert(guild_id.0, guild_voice_config);
guilds_voice_config.insert(guild_id, guild_voice_config);
guilds_voice_config.write();
drop(guilds_voice_config);
let _ = params.channel_id.say(&ctx, answer_msg).await;
Expand Down
19 changes: 12 additions & 7 deletions src/red_alert/commands_handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use voskrust::api::Model as VoskModel;

pub struct RedAlertCommandsHandlerConstructor {
pub recognition_model: VoskModel,
pub listening_text: Option<String>,
pub red_alert_handler: Arc<RedAlertHandler>,
pub l10n: L10n,
}
Expand All @@ -40,14 +39,20 @@ impl RedAlertCommandsHandlerConstructor {
l10n: self.l10n.clone(),
}),
on_ready: Box::new(RedAlertOnReady {
guilds_voices_receivers: guilds_voices_receivers.clone(),
actions_history: actions_history.clone(),
guilds_voice_config: guilds_voice_config.clone(),
recognition_model: self.recognition_model,
listening_text: self.listening_text,
red_alert_handler: self.red_alert_handler.clone(),
monitoring_performer: RedAlertMonitoringPerformer {
guilds_voices_receivers: guilds_voices_receivers.clone(),
guilds_voice_config: guilds_voice_config.clone(),
},
recognizer_performer: RedAlertRecognizerPerformer {
guilds_voices_receivers: guilds_voices_receivers.clone(),
actions_history: actions_history.clone(),
guilds_voice_config: guilds_voice_config.clone(),
recognition_model: self.recognition_model,
red_alert_handler: self.red_alert_handler.clone(),
},
cancel_recognizer_sender: Arc::new(Mutex::new(None)),
cancel_monitoring_sender: Arc::new(Mutex::new(None)),
l10n: self.l10n.clone(),
}),
commands: vec![
Box::new(TextRedAlertCommand {
Expand Down
240 changes: 23 additions & 217 deletions src/red_alert/commands_handler/on_ready.rs
Original file line number Diff line number Diff line change
@@ -1,233 +1,39 @@
use super::*;
use serenity::model::gateway::Activity;
use serenity::model::id::GuildId;
use serenity::model::prelude::{ChannelId, OnlineStatus, Ready, UserId};
use serenity::model::prelude::{OnlineStatus, Ready};
use serenity::prelude::Context;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::oneshot::{channel, Sender};
use tokio::sync::{Mutex, RwLock};
use voskrust::api::Model as VoskModel;
use tokio::sync::oneshot::Sender;
use tokio::sync::Mutex;

pub(super) struct RedAlertOnReady {
pub(super) guilds_voices_receivers: Arc<RwLock<HashMap<GuildId, VoiceReceiver>>>,
pub(super) actions_history: Arc<Mutex<RedAlertActionsHistory>>,
pub(super) guilds_voice_config: Arc<RwLock<RedAlertGuildsVoiceConfig>>,
pub(super) recognition_model: VoskModel,
pub(super) listening_text: Option<String>,
pub(super) red_alert_handler: Arc<RedAlertHandler>,
pub(super) monitoring_performer: RedAlertMonitoringPerformer,
pub(super) recognizer_performer: RedAlertRecognizerPerformer,
pub(super) cancel_recognizer_sender: Arc<Mutex<Option<Sender<()>>>>,
pub(super) cancel_monitoring_sender: Arc<Mutex<Option<Sender<()>>>>,
}

impl RedAlertOnReady {
async fn start_recognizer(&self, ctx: &Context) {
let (tx, mut rx) = channel::<()>();
let mut cancel_sender = self.cancel_recognizer_sender.lock().await;
*cancel_sender = Some(tx);
drop(cancel_sender);
let guilds_voices_receivers = self.guilds_voices_receivers.clone();
let actions_history = self.actions_history.clone();
let recognition_model = self.recognition_model.clone();
let guilds_voice_config = self.guilds_voice_config.clone();
let red_alert_handler = self.red_alert_handler.clone();
let ctx = ctx.clone();
tokio::spawn(async move {
let mut recognizer_signal = Recognizer {
model: recognition_model,
voices_queue: GuildsVoicesReceivers(guilds_voices_receivers),
}
.start();
let mut authors_processed_kicks: HashMap<UserId, HashSet<UserId>> = HashMap::new();
loop {
let Some(recognizer_state) = tokio::select! {
recognizer_state = recognizer_signal.recv() => recognizer_state,
_ = &mut rx => None,
} else {
break;
};
let log_prefix = match recognizer_state {
RecognizerState::RecognitionStart(info)
| RecognizerState::RecognitionResult(info, _)
| RecognizerState::RecognitionEnd(info) => {
let mut prefix_parts: Vec<String> = vec![];
let guild_id = info.guild_id;
if let Some(guild) = ctx.cache.guild(guild_id) {
prefix_parts.push(format!("[G:{}]", guild.name));
} else {
prefix_parts.push(format!("[GID:{}]", guild_id));
}
let user_id = info.user_id;
if let Some(user) = ctx.cache.user(user_id) {
prefix_parts.push(format!(
"[U:{}#{:04}]",
user.name,
user.discriminator.min(9999).max(1)
));
} else {
prefix_parts.push(format!("[UID:{}]", user_id));
}
prefix_parts.join("")
}
};
match recognizer_state {
RecognizerState::RecognitionResult(info, result) => {
info!(
"{} Recognition RESULT: type: {:?}, text: \"{}\".",
log_prefix, result.result_type, result.text
);
let guilds_voice_config = guilds_voice_config.read().await;
let users_ids_kicks_reasons = guilds_voice_config
.get(&info.guild_id)
.should_kick(&info.user_id.0, &result.text)
.into_iter()
.map(|v| (UserId(*v.0), v.1))
.collect::<HashMap<UserId, RedAlertVoiceSearchResult>>();
drop(guilds_voice_config);
let mut users_ids_kicks = users_ids_kicks_reasons
.keys()
.cloned()
.collect::<HashSet<UserId>>();
if let Some(mut author_processed_kicks) =
authors_processed_kicks.remove(&info.user_id)
{
users_ids_kicks = &users_ids_kicks - &author_processed_kicks;
author_processed_kicks.extend(users_ids_kicks.clone());
authors_processed_kicks.insert(info.user_id, author_processed_kicks);
} else {
authors_processed_kicks.insert(info.user_id, users_ids_kicks.clone());
}
if users_ids_kicks.is_empty() {
continue;
};
for (kick_user_id, kick_reason) in users_ids_kicks_reasons {
if !users_ids_kicks.contains(&kick_user_id) {
continue;
}
info!(
"{} Recognition RESULT will be used for kick. Have restriction \"{}\"({}) =~ \"{}\".",
log_prefix,
kick_reason.real_word,
kick_reason.total_similarity,
kick_reason.word
);
let actions_history = actions_history.clone();
let red_alert_handler = red_alert_handler.clone();
let ctx = ctx.clone();
let log_prefix = log_prefix.clone();
let result_text = result.text.clone();
tokio::spawn(async move {
let guild_id = info.guild_id;
let deportation_result = red_alert_handler
.single(&ctx, &guild_id, &kick_user_id)
.await;
info!(
"{} Recognition RESULT used for kick, status is {:?}.",
log_prefix, deportation_result
);
actions_history.lock().await.log_history(
guild_id,
RedAlertActionType::Voice {
author_id: info.user_id,
target_id: kick_user_id,
full_text: result_text,
reason: kick_reason,
is_success: deportation_result.is_deported(),
},
);
});
}
}
RecognizerState::RecognitionStart(info) => {
info!("{} Recognition STARTED.", log_prefix);
authors_processed_kicks.remove(&info.user_id);
}
RecognizerState::RecognitionEnd(info) => {
info!("{} Recognition ENDED.", log_prefix);
authors_processed_kicks.remove(&info.user_id);
}
}
}
});
}
async fn start_monitoring(&self, ctx: &Context) {
let (tx, mut rx) = channel::<()>();
let mut cancel_sender = self.cancel_monitoring_sender.lock().await;
*cancel_sender = Some(tx);
drop(cancel_sender);
let guilds_voices_receivers = self.guilds_voices_receivers.clone();
let guilds_voice_config = self.guilds_voice_config.clone();
let ctx = ctx.clone();
tokio::spawn(async move {
let mut guilds_active_channels: HashMap<GuildId, ChannelId> = HashMap::new();
loop {
let Some(_) = tokio::select! {
_ = tokio::time::sleep(Duration::from_secs(1)) => Some(()),
_ = &mut rx => None,
} else {
break;
};
let bot_user_id = ctx.cache.current_user_id();
let guilds_voice_config = guilds_voice_config.read().await;
for guild_id in &guilds_voice_config.auto_track_ids {
let Some(guild) = ctx.cache.guild(*guild_id) else {
continue;
};
let mut channels_users_counts: HashMap<ChannelId, u8> = HashMap::new();
for (user_id, voice_state) in guild.voice_states {
if bot_user_id == user_id {
continue;
}
let Some(channel_id) = voice_state.channel_id else {
continue;
};
if let Some(users_count) = channels_users_counts.remove(&channel_id) {
channels_users_counts.insert(channel_id, users_count + 1);
} else {
channels_users_counts.insert(channel_id, 1);
}
}
if let Some(channel_id) = {
let mut channels_users_counts = channels_users_counts
.into_iter()
.collect::<Vec<(ChannelId, u8)>>();
channels_users_counts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
channels_users_counts.first().map(|c| c.0)
} {
let is_prev_channel = guilds_active_channels
.get(&guild.id)
.map_or_else(|| false, |i| i == &channel_id);
if is_prev_channel {
continue;
}
guilds_active_channels.insert(guild.id, channel_id);
_ = start_listen(
guilds_voices_receivers.clone(),
&ctx,
guild.id,
channel_id,
)
.await;
} else {
if !guilds_active_channels.remove(&guild.id).is_some() {
continue;
}
_ = stop_listen(guilds_voices_receivers.clone(), &ctx, guild.id).await;
}
}
}
});
}
pub(super) l10n: L10n,
}

#[async_trait]
impl OnReady for RedAlertOnReady {
async fn process(&self, ctx: Context, ready: Ready) {
info!("{} is connected!", ready.user.name);
let activity = self.listening_text.as_ref().map(|t| Activity::listening(t));
ctx.set_presence(activity, OnlineStatus::Online).await;
self.start_recognizer(&ctx).await;
self.start_monitoring(&ctx).await;
ctx.set_presence(
Some(Activity::listening(
self.l10n.string("listening-text", fluent_args![]),
)),
OnlineStatus::Online,
)
.await;

let new_cancel_monitoring_sender = self.monitoring_performer.perform(&ctx);
let mut cancel_monitoring_sender = self.cancel_monitoring_sender.lock().await;
*cancel_monitoring_sender = Some(new_cancel_monitoring_sender);
drop(cancel_monitoring_sender);

let new_cancel_recognizer_sender = self.recognizer_performer.perform(&ctx);
let mut cancel_recognizer_sender = self.cancel_recognizer_sender.lock().await;
*cancel_recognizer_sender = Some(new_cancel_recognizer_sender);
drop(cancel_recognizer_sender);
}
}
Loading

0 comments on commit 69ea55f

Please sign in to comment.