1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
//! Requires the "client", "standard_framework", and "voice" features be enabled
//! in your Cargo.toml, like so:
//!
//! ```toml
//! [dependencies.serenity]
//! git = "https://github.com/serenity-rs/serenity.git"
//! features = ["client", "standard_framework", "voice"]
//! ```
#[macro_use] extern crate serenity;
extern crate typemap;
use std::{env, sync::Arc};
use serenity::{
client::{bridge::voice::ClientVoiceManager, CACHE, Client, Context, EventHandler},
framework::StandardFramework,
model::{channel::Message, gateway::Ready, id::ChannelId, misc::Mentionable},
prelude::*,
voice::AudioReceiver,
Result as SerenityResult,
};
use typemap::Key;
struct VoiceManager;
impl Key for VoiceManager {
type Value = Arc<Mutex<ClientVoiceManager>>;
}
struct Handler;
impl EventHandler for Handler {
fn ready(&self, _: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);
}
}
struct Receiver;
impl Receiver {
pub fn new() -> Self {
// You can manage state here, such as a buffer of audio packet bytes so
// you can later store them in intervals.
Self { }
}
}
impl AudioReceiver for Receiver {
fn speaking_update(&mut self, _ssrc: u32, _user_id: u64, _speaking: bool) {
// You can implement logic here so that you can differentiate users'
// SSRCs and map the SSRC to the User ID and maintain a state in
// `Receiver`. Using this map, you can map the `ssrc` in `voice_packet`
// to the user ID and handle their audio packets separately.
}
fn voice_packet(&mut self, ssrc: u32, sequence: u16, _timestamp: u32, _stereo: bool, data: &[i16]) {
println!("Audio packet's first 5 bytes: {:?}", data.get(..5));
println!(
"Audio packet sequence {:05} has {:04} bytes, SSRC {}",
sequence,
data.len(),
ssrc,
);
}
}
fn main() {
// Configure the client with your Discord bot token in the environment.
let token = env::var("DISCORD_TOKEN")
.expect("Expected a token in the environment");
let mut client = Client::new(&token, Handler).expect("Err creating client");
// Obtain a lock to the data owned by the client, and insert the client's
// voice manager into it. This allows the voice manager to be accessible by
// event handlers and framework commands.
{
let mut data = client.data.lock();
data.insert::<VoiceManager>(Arc::clone(&client.voice_manager));
}
client.with_framework(StandardFramework::new()
.configure(|c| c
.prefix("~")
.on_mention(true))
.cmd("join", join)
.cmd("leave", leave)
.cmd("ping", ping));
let _ = client.start().map_err(|why| println!("Client ended: {:?}", why));
}
command!(join(ctx, msg, args) {
let connect_to = match args.single::<u64>() {
Ok(id) => ChannelId(id),
Err(_) => {
check_msg(msg.reply("Requires a valid voice channel ID be given"));
return Ok(());
},
};
let guild_id = match CACHE.read().guild_channel(msg.channel_id) {
Some(channel) => channel.read().guild_id,
None => {
check_msg(msg.channel_id.say("Groups and DMs not supported"));
return Ok(());
},
};
let mut manager_lock = ctx.data.lock().get::<VoiceManager>().cloned().expect("Expected VoiceManager in ShareMap.");
let mut manager = manager_lock.lock();
if let Some(handler) = manager.join(guild_id, connect_to) {
handler.listen(Some(Box::new(Receiver::new())));
check_msg(msg.channel_id.say(&format!("Joined {}", connect_to.mention())));
} else {
check_msg(msg.channel_id.say("Error joining the channel"));
}
});
command!(leave(ctx, msg) {
let guild_id = match CACHE.read().guild_channel(msg.channel_id) {
Some(channel) => channel.read().guild_id,
None => {
check_msg(msg.channel_id.say("Groups and DMs not supported"));
return Ok(());
},
};
let mut manager_lock = ctx.data.lock().get::<VoiceManager>().cloned().expect("Expected VoiceManager in ShareMap.");
let mut manager = manager_lock.lock();
let has_handler = manager.get(guild_id).is_some();
if has_handler {
manager.remove(guild_id);
check_msg(msg.channel_id.say("Left voice channel"));
} else {
check_msg(msg.reply("Not in a voice channel"));
}
});
command!(ping(_context, msg) {
check_msg(msg.channel_id.say("Pong!"));
});
/// Checks that a message successfully sent; if not, then logs why to stdout.
fn check_msg(result: SerenityResult<Message>) {
if let Err(why) = result {
println!("Error sending message: {:?}", why);
}
}
|