first commit

This commit is contained in:
mii
2023-10-19 07:24:22 +00:00
commit d7ebba0f9f
7 changed files with 2458 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
config.toml

2221
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

30
Cargo.toml Normal file
View File

@ -0,0 +1,30 @@
[package]
name = "audio-receiver"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
toml = "0.5"
async-trait = "0.1.57"
hound = "3.5.1"
byteorder = "1.3.4"
serde_json = "1.0"
serde = "1.0"
[dependencies.uuid]
version = "0.8"
features = ["serde", "v4"]
[dependencies.songbird]
version = "0.3.2"
features = ["builtin-queue"]
[dependencies.serenity]
version = "0.11.6"
features = ["builder", "cache", "client", "gateway", "model", "utils", "unstable_discord_api", "collector", "rustls_backend", "framework", "voice"]
[dependencies.tokio]
version = "1.0"
features = ["macros", "rt-multi-thread"]

127
src/audio_receiver.rs Normal file
View File

@ -0,0 +1,127 @@
use std::{collections::HashMap, io::Write, sync::Arc};
use async_trait::async_trait;
use byteorder::{LittleEndian, WriteBytesExt};
use serenity::{futures::lock::Mutex, model::channel::Message, prelude::Context};
use songbird::{
model::payload::Speaking, CoreEvent, Event, EventContext, EventHandler as VoiceEventHandler,
};
#[derive(Clone)]
struct Receiver {
pub ssrc_map: Arc<Mutex<HashMap<u32, u64>>>,
pub audio_data: Arc<Mutex<HashMap<u32, Vec<i16>>>>,
}
impl Receiver {
pub fn new() -> Self {
Self {
ssrc_map: Arc::new(Mutex::new(HashMap::new())),
audio_data: Arc::new(Mutex::new(HashMap::new())),
}
}
}
fn pcm_to_wav(pcm: Vec<i16>) -> Vec<u8> {
let spec = hound::WavSpec {
channels: 2,
sample_rate: 48000,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
let mut wav: Vec<u8> = vec![];
wav.write_all(&spec.into_header_for_infinite_file())
.unwrap();
for &n in &pcm {
wav.write_i16::<LittleEndian>(n).unwrap();
}
wav
}
#[async_trait]
impl VoiceEventHandler for Receiver {
async fn act(&self, ctx: &EventContext<'_>) -> Option<Event> {
match ctx {
EventContext::SpeakingStateUpdate(Speaking { ssrc, user_id, .. }) => {
self.ssrc_map.lock().await.insert(*ssrc, user_id.unwrap().0);
}
EventContext::SpeakingUpdate(data) => {
if !data.speaking {
let audio =
if let Some(audio) = self.audio_data.lock().await.clone().get(&data.ssrc) {
Some(audio.clone())
} else {
None
};
if !self.ssrc_map.lock().await.contains_key(&data.ssrc) {
return None;
}
if let Some(audio) = audio {
let wav = pcm_to_wav(audio.clone());
let mut file =
std::fs::File::create(format!("./audio/{}.wav", data.ssrc)).unwrap();
file.write_all(&wav).unwrap();
file.flush().unwrap();
self.audio_data.lock().await.remove(&data.ssrc);
}
}
}
EventContext::VoicePacket(data) => {
let ssrc = data.packet.ssrc;
if let Some(audio) = data.audio {
if !self.audio_data.lock().await.contains_key(&ssrc) {
self.audio_data.lock().await.insert(ssrc, vec![]);
}
let mut prev_data = self.audio_data.lock().await.get(&ssrc).unwrap().clone();
prev_data.append(&mut audio.clone());
self.audio_data.lock().await.insert(ssrc, prev_data);
}
}
_ => {}
}
None
}
}
pub async fn audio_receive(ctx: &Context, message: &Message) {
let guild = message
.guild_id
.unwrap()
.to_guild_cached(&ctx.cache)
.unwrap();
let channel_id = guild
.voice_states
.get(&message.author.id)
.and_then(|state| state.channel_id);
if channel_id.is_none() {
return;
}
let manager = songbird::get(ctx).await.unwrap();
let (handler_lock, conn_result) = manager.join(guild.id.0, channel_id.unwrap().0).await;
let receiver = Receiver::new();
if let Ok(_) = conn_result {
let mut handler = handler_lock.lock().await;
handler.add_global_event(CoreEvent::SpeakingStateUpdate.into(), receiver.clone());
handler.add_global_event(CoreEvent::SpeakingUpdate.into(), receiver.clone());
handler.add_global_event(CoreEvent::VoicePacket.into(), receiver.clone());
handler.add_global_event(CoreEvent::RtcpPacket.into(), receiver.clone());
handler.add_global_event(CoreEvent::ClientDisconnect.into(), receiver);
}
}

7
src/config.rs Normal file
View File

@ -0,0 +1,7 @@
use serde::Deserialize;
#[derive(Deserialize, Debug, Clone)]
pub struct Config {
pub token: String,
pub application_id: u64,
}

22
src/event_handler.rs Normal file
View File

@ -0,0 +1,22 @@
use async_trait::async_trait;
use serenity::{
model::{channel::Message, prelude::Ready},
prelude::{Context, EventHandler},
};
use crate::audio_receiver::audio_receive;
pub struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, message: Message) {
if message.content == "audio-receive" {
audio_receive(&ctx, &message).await;
}
}
async fn ready(&self, _ctx: Context, _ready: Ready) {
println!("bot ready");
}
}

49
src/main.rs Normal file
View File

@ -0,0 +1,49 @@
use std::env;
use config::Config;
use event_handler::Handler;
use serenity::{framework::StandardFramework, prelude::GatewayIntents, Client};
use songbird::{driver::DecodeMode, SerenityInit};
mod audio_receiver;
mod config;
mod event_handler;
async fn create_client(token: &str, id: u64) -> Result<Client, serenity::Error> {
let framework = StandardFramework::new().configure(|c| c.with_whitespace(true));
let songbird_config = songbird::Config::default().decode_mode(DecodeMode::Decode);
Client::builder(token, GatewayIntents::all())
.event_handler(Handler)
.application_id(id)
.framework(framework)
.register_songbird_from_config(songbird_config)
.await
}
#[tokio::main]
async fn main() {
let config = {
let config = std::fs::read_to_string("./config.toml");
if let Ok(config) = config {
toml::from_str::<Config>(&config).expect("Cannot load config file.")
} else {
let token = env::var("BOT_TOKEN").unwrap();
let application_id = env::var("BOT_ID").unwrap();
Config {
token,
application_id: u64::from_str_radix(&application_id, 10).unwrap(),
}
}
};
let mut client = create_client(&config.token, config.application_id)
.await
.expect("Err creating client");
if let Err(err) = client.start().await {
println!("Client error: {err:?}");
}
}