pub struct Http {
pub client: Client,
pub base_url: String,
/* private fields */
}Expand description
HTTP client for making REST API calls.
Created automatically by the client builder. Available as ctx.http in event handlers
or directly if you just need REST calls without a gateway connection:
use fluxer::http::Http;
#[tokio::main]
async fn main() {
let http = Http::new("your-bot-token", "https://api.fluxer.app/v1".to_string());
let me = http.get_me().await.unwrap();
println!("Bot user: {}", me.username);
}Fields§
§client: Client§base_url: StringImplementations§
Source§impl Http
impl Http
Sourcepub fn new(token: &str, base_url: String) -> Self
pub fn new(token: &str, base_url: String) -> Self
Creates a new HTTP client. The token is sent as Bot {token} in the
Authorization header on every request.
Sourcepub fn new_user(token: &str, base_url: String) -> Self
pub fn new_user(token: &str, base_url: String) -> Self
Creates a new HTTP client for a user token (no Bot prefix).
pub fn get_token(&self) -> &str
Sourcepub async fn get_gateway(&self) -> Result<String, ClientError>
pub async fn get_gateway(&self) -> Result<String, ClientError>
Fetches the gateway URL. Used internally during connection setup.
Sourcepub async fn get_me(&self) -> Result<User, ClientError>
pub async fn get_me(&self) -> Result<User, ClientError>
Fetches the bot’s own user object.
Sourcepub async fn get_current_user_guilds(&self) -> Result<Vec<Guild>, ClientError>
pub async fn get_current_user_guilds(&self) -> Result<Vec<Guild>, ClientError>
Returns all guilds the bot is in.
Sourcepub async fn get_channel(
&self,
channel_id: &str,
) -> Result<Channel, ClientError>
pub async fn get_channel( &self, channel_id: &str, ) -> Result<Channel, ClientError>
Fetches a channel by ID.
Sourcepub async fn edit_channel(
&self,
channel_id: &str,
payload: &ChannelCreatePayload,
) -> Result<Channel, ClientError>
pub async fn edit_channel( &self, channel_id: &str, payload: &ChannelCreatePayload, ) -> Result<Channel, ClientError>
Edits a channel. Only the fields you set in the payload will change.
Sourcepub async fn delete_channel(&self, channel_id: &str) -> Result<(), ClientError>
pub async fn delete_channel(&self, channel_id: &str) -> Result<(), ClientError>
Permanently deletes a channel. Can’t be undone.
Sourcepub async fn trigger_typing(&self, channel_id: &str) -> Result<(), ClientError>
pub async fn trigger_typing(&self, channel_id: &str) -> Result<(), ClientError>
Triggers the “Bot is typing…” indicator. Lasts ~10 seconds or until the bot sends a message. (I actually haven’t tested this)
Sourcepub async fn get_messages(
&self,
channel_id: &str,
query: GetMessagesQuery,
) -> Result<Vec<Message>, ClientError>
pub async fn get_messages( &self, channel_id: &str, query: GetMessagesQuery, ) -> Result<Vec<Message>, ClientError>
Fetches messages from a channel. Use GetMessagesQuery to paginate.
use fluxer::prelude::*;
let query = GetMessagesQuery {
limit: Some(10),
..Default::default()
};
let messages = http.get_messages("channel_id", query).await.unwrap();Examples found in repository?
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }More examples
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn get_message(
&self,
channel_id: &str,
message_id: &str,
) -> Result<Message, ClientError>
pub async fn get_message( &self, channel_id: &str, message_id: &str, ) -> Result<Message, ClientError>
Fetches a single message by ID.
Sourcepub async fn send_message(
&self,
channel_id: &str,
content: &str,
) -> Result<Message, ClientError>
pub async fn send_message( &self, channel_id: &str, content: &str, ) -> Result<Message, ClientError>
Sends a text message. For embeds or other options, use
send_message_advanced.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 if msg.author.bot.unwrap_or(false) {
29 return;
30 }
31
32 let content = match msg.content.as_deref() {
33 Some(c) => c,
34 None => return,
35 };
36
37 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
38 let guild_id = match msg.guild_id.as_deref() {
39 Some(id) => id,
40 None => return,
41 };
42
43 let (cmd, args) = match parse_command(content) {
44 Some(v) => v,
45 None => return,
46 };
47
48 match cmd {
49 "join" => {
50 if args.is_empty() {
51 let _ = ctx.http.send_message(channel_id, "`!join <voice_channel_id>`").await;
52 return;
53 }
54
55 match ctx.join_voice(guild_id, args).await {
56 Ok(conn) => {
57 *self.voice.lock().await = Some(conn);
58 let _ = ctx.http.send_message(channel_id, "Joined.").await;
59 }
60 Err(e) => {
61 let _ = ctx.http.send_message(channel_id, &format!("Failed: {}", e)).await;
62 }
63 }
64 }
65
66 "leave" => {
67 if let Some(handle) = self.playback.lock().await.take() {
68 handle.abort();
69 }
70 *self.voice.lock().await = None;
71 let _ = ctx.leave_voice(guild_id).await;
72 let _ = ctx.http.send_message(channel_id, "Left.").await;
73 }
74
75 "play" => {
76 let conn = self.voice.lock().await;
77 let conn = match conn.as_ref() {
78 Some(c) => c,
79 None => {
80 let _ = ctx.http.send_message(channel_id, "Not in a voice channel.").await;
81 return;
82 }
83 };
84
85 if let Some(handle) = self.playback.lock().await.take() {
86 handle.abort();
87 }
88
89 match conn.play_music(AUDIO_FILE, ctx.http.clone(), channel_id.to_string()).await {
90 Ok(handle) => {
91 *self.playback.lock().await = Some(handle);
92 let _ = ctx.http.send_message(channel_id, &format!("Playing `{}`.", AUDIO_FILE)).await;
93 }
94 Err(e) => {
95 let _ = ctx.http.send_message(channel_id, &format!("Failed: {}", e)).await;
96 }
97 }
98 }
99
100 "stop" => {
101 if let Some(handle) = self.playback.lock().await.take() {
102 handle.abort();
103 let _ = ctx.http.send_message(channel_id, "Stopped.").await;
104 } else {
105 let _ = ctx.http.send_message(channel_id, "Nothing is playing.").await;
106 }
107 }
108
109 _ => {}
110 }
111 }More examples
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn send_message_advanced(
&self,
channel_id: &str,
payload: &MessageCreatePayload,
) -> Result<Message, ClientError>
pub async fn send_message_advanced( &self, channel_id: &str, payload: &MessageCreatePayload, ) -> Result<Message, ClientError>
Sends a message with full control over the payload (embeds, TTS, replies, etc).
Sourcepub async fn send_embed(
&self,
channel_id: &str,
content: Option<&str>,
embeds: Vec<Embed>,
) -> Result<Message, ClientError>
pub async fn send_embed( &self, channel_id: &str, content: Option<&str>, embeds: Vec<Embed>, ) -> Result<Message, ClientError>
Shorthand for sending embeds. Wraps send_message_advanced.
Examples found in repository?
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }More examples
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn reply_to_message(
&self,
channel_id: &str,
message_id: &str,
content: &str,
) -> Result<Message, ClientError>
pub async fn reply_to_message( &self, channel_id: &str, message_id: &str, content: &str, ) -> Result<Message, ClientError>
Replies to a specific message. The client will render it as a reply thread. Fails with a 404 if the referenced message no longer exists.
Sourcepub async fn edit_message(
&self,
channel_id: &str,
message_id: &str,
content: &str,
) -> Result<Message, ClientError>
pub async fn edit_message( &self, channel_id: &str, message_id: &str, content: &str, ) -> Result<Message, ClientError>
Edits a message’s content. Bot must be the author.
Examples found in repository?
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }More examples
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn edit_message_advanced(
&self,
channel_id: &str,
message_id: &str,
payload: &MessageCreatePayload,
) -> Result<Message, ClientError>
pub async fn edit_message_advanced( &self, channel_id: &str, message_id: &str, payload: &MessageCreatePayload, ) -> Result<Message, ClientError>
Edits a message with full control over the payload.
Sourcepub async fn delete_message(
&self,
channel_id: &str,
message_id: &str,
) -> Result<(), ClientError>
pub async fn delete_message( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>
Deletes a message. Bot must be the author, or have Manage Messages.
Examples found in repository?
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }More examples
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn bulk_delete_messages(
&self,
channel_id: &str,
message_ids: Vec<&str>,
) -> Result<(), ClientError>
pub async fn bulk_delete_messages( &self, channel_id: &str, message_ids: Vec<&str>, ) -> Result<(), ClientError>
Deletes multiple messages at once. Way faster than deleting one by one.
Examples found in repository?
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }More examples
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn add_reaction(
&self,
channel_id: &str,
message_id: &str,
emoji: &str,
) -> Result<(), ClientError>
pub async fn add_reaction( &self, channel_id: &str, message_id: &str, emoji: &str, ) -> Result<(), ClientError>
Reacts to a message as the bot. emoji should be a unicode emoji like
"👍" or a custom emoji in name:id format. You can use
Emoji::to_reaction_string to get the right format.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn remove_own_reaction(
&self,
channel_id: &str,
message_id: &str,
emoji: &str,
) -> Result<(), ClientError>
pub async fn remove_own_reaction( &self, channel_id: &str, message_id: &str, emoji: &str, ) -> Result<(), ClientError>
Removes the bot’s own reaction from a message.
Sourcepub async fn remove_user_reaction(
&self,
channel_id: &str,
message_id: &str,
emoji: &str,
user_id: &str,
) -> Result<(), ClientError>
pub async fn remove_user_reaction( &self, channel_id: &str, message_id: &str, emoji: &str, user_id: &str, ) -> Result<(), ClientError>
Removes someone else’s reaction. Needs Manage Messages permission.
Sourcepub async fn get_reactions(
&self,
channel_id: &str,
message_id: &str,
emoji: &str,
) -> Result<Vec<User>, ClientError>
pub async fn get_reactions( &self, channel_id: &str, message_id: &str, emoji: &str, ) -> Result<Vec<User>, ClientError>
Gets the list of users who reacted with a specific emoji.
Sourcepub async fn clear_reactions(
&self,
channel_id: &str,
message_id: &str,
) -> Result<(), ClientError>
pub async fn clear_reactions( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>
Removes all reactions from a message. Needs Manage Messages.
Sourcepub async fn clear_reactions_for_emoji(
&self,
channel_id: &str,
message_id: &str,
emoji: &str,
) -> Result<(), ClientError>
pub async fn clear_reactions_for_emoji( &self, channel_id: &str, message_id: &str, emoji: &str, ) -> Result<(), ClientError>
Removes all reactions for a specific emoji. Needs Manage Messages.
Sourcepub async fn get_pins(
&self,
channel_id: &str,
) -> Result<PinsResponse, ClientError>
pub async fn get_pins( &self, channel_id: &str, ) -> Result<PinsResponse, ClientError>
Fetches pinned messages in a channel.
Sourcepub async fn pin_message(
&self,
channel_id: &str,
message_id: &str,
) -> Result<(), ClientError>
pub async fn pin_message( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>
Pins a message. Needs Manage Messages.
Sourcepub async fn unpin_message(
&self,
channel_id: &str,
message_id: &str,
) -> Result<(), ClientError>
pub async fn unpin_message( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>
Unpins a message. Needs Manage Messages.
Sourcepub async fn get_invite(&self, invite_code: &str) -> Result<Invite, ClientError>
pub async fn get_invite(&self, invite_code: &str) -> Result<Invite, ClientError>
Fetches an invite by code. Includes approximate member counts.
Sourcepub async fn create_invite(
&self,
channel_id: &str,
payload: &CreateInvitePayload,
) -> Result<Invite, ClientError>
pub async fn create_invite( &self, channel_id: &str, payload: &CreateInvitePayload, ) -> Result<Invite, ClientError>
Creates an invite for a channel.
Sourcepub async fn delete_invite(&self, invite_code: &str) -> Result<(), ClientError>
pub async fn delete_invite(&self, invite_code: &str) -> Result<(), ClientError>
Deletes an invite by code.
Sourcepub async fn get_channel_invites(
&self,
channel_id: &str,
) -> Result<Vec<Invite>, ClientError>
pub async fn get_channel_invites( &self, channel_id: &str, ) -> Result<Vec<Invite>, ClientError>
Returns all active invites for a channel.
Sourcepub async fn get_guild_invites(
&self,
guild_id: &str,
) -> Result<Vec<Invite>, ClientError>
pub async fn get_guild_invites( &self, guild_id: &str, ) -> Result<Vec<Invite>, ClientError>
Returns all active invites for a guild.
Sourcepub async fn get_guild(&self, guild_id: &str) -> Result<Guild, ClientError>
pub async fn get_guild(&self, guild_id: &str) -> Result<Guild, ClientError>
Fetches a guild by ID.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn edit_guild(
&self,
guild_id: &str,
payload: &EditGuildPayload,
) -> Result<Guild, ClientError>
pub async fn edit_guild( &self, guild_id: &str, payload: &EditGuildPayload, ) -> Result<Guild, ClientError>
Edits guild settings. Only fields you set in the payload will change.
Sourcepub async fn delete_guild(&self, guild_id: &str) -> Result<(), ClientError>
pub async fn delete_guild(&self, guild_id: &str) -> Result<(), ClientError>
Permanently deletes a guild. The bot must be the owner. (not tested)
Sourcepub async fn get_guild_channels(
&self,
guild_id: &str,
) -> Result<Vec<Channel>, ClientError>
pub async fn get_guild_channels( &self, guild_id: &str, ) -> Result<Vec<Channel>, ClientError>
Returns all channels in a guild.
Sourcepub async fn create_channel(
&self,
guild_id: &str,
payload: &ChannelCreatePayload,
) -> Result<Channel, ClientError>
pub async fn create_channel( &self, guild_id: &str, payload: &ChannelCreatePayload, ) -> Result<Channel, ClientError>
Creates a channel in a guild. You need at least name in the payload.
Sourcepub async fn get_guild_member(
&self,
guild_id: &str,
user_id: &str,
) -> Result<Member, ClientError>
pub async fn get_guild_member( &self, guild_id: &str, user_id: &str, ) -> Result<Member, ClientError>
Fetches a single guild member.
Sourcepub async fn get_guild_members(
&self,
guild_id: &str,
limit: Option<u16>,
after: Option<&str>,
) -> Result<Vec<Member>, ClientError>
pub async fn get_guild_members( &self, guild_id: &str, limit: Option<u16>, after: Option<&str>, ) -> Result<Vec<Member>, ClientError>
Fetches guild members. limit caps at 1000, after is a user ID for pagination.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn kick_member(
&self,
guild_id: &str,
user_id: &str,
) -> Result<(), ClientError>
pub async fn kick_member( &self, guild_id: &str, user_id: &str, ) -> Result<(), ClientError>
Kicks a member from the guild.
Sourcepub async fn edit_member(
&self,
guild_id: &str,
user_id: &str,
payload: &EditMemberPayload,
) -> Result<Member, ClientError>
pub async fn edit_member( &self, guild_id: &str, user_id: &str, payload: &EditMemberPayload, ) -> Result<Member, ClientError>
Edits a member’s properties. To clear a nullable field like nick,
set it to Some(None).
Sourcepub async fn add_member_role(
&self,
guild_id: &str,
user_id: &str,
role_id: &str,
) -> Result<(), ClientError>
pub async fn add_member_role( &self, guild_id: &str, user_id: &str, role_id: &str, ) -> Result<(), ClientError>
Grants a role to a guild member. Requires Manage Roles.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn remove_member_role(
&self,
guild_id: &str,
user_id: &str,
role_id: &str,
) -> Result<(), ClientError>
pub async fn remove_member_role( &self, guild_id: &str, user_id: &str, role_id: &str, ) -> Result<(), ClientError>
Removes a role from a guild member. Requires Manage Roles.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn ban_member(
&self,
guild_id: &str,
user_id: &str,
reason: &str,
) -> Result<(), ClientError>
pub async fn ban_member( &self, guild_id: &str, user_id: &str, reason: &str, ) -> Result<(), ClientError>
Bans a member. reason is stored in the audit log.
Sourcepub async fn unban_member(
&self,
guild_id: &str,
user_id: &str,
) -> Result<(), ClientError>
pub async fn unban_member( &self, guild_id: &str, user_id: &str, ) -> Result<(), ClientError>
Removes a ban.
Sourcepub async fn get_guild_bans(
&self,
guild_id: &str,
) -> Result<Vec<Value>, ClientError>
pub async fn get_guild_bans( &self, guild_id: &str, ) -> Result<Vec<Value>, ClientError>
Returns the guild’s ban list.
Sourcepub async fn get_guild_roles(
&self,
guild_id: &str,
) -> Result<Vec<Role>, ClientError>
pub async fn get_guild_roles( &self, guild_id: &str, ) -> Result<Vec<Role>, ClientError>
Returns all roles in a guild.
Sourcepub async fn create_role(
&self,
guild_id: &str,
payload: &CreateRolePayload,
) -> Result<Role, ClientError>
pub async fn create_role( &self, guild_id: &str, payload: &CreateRolePayload, ) -> Result<Role, ClientError>
Creates a new role in a guild.
Sourcepub async fn edit_role(
&self,
guild_id: &str,
role_id: &str,
payload: &EditRolePayload,
) -> Result<Role, ClientError>
pub async fn edit_role( &self, guild_id: &str, role_id: &str, payload: &EditRolePayload, ) -> Result<Role, ClientError>
Edits a role’s properties.
Sourcepub async fn delete_role(
&self,
guild_id: &str,
role_id: &str,
) -> Result<(), ClientError>
pub async fn delete_role( &self, guild_id: &str, role_id: &str, ) -> Result<(), ClientError>
Deletes a role.
Sourcepub async fn get_guild_emojis(
&self,
guild_id: &str,
) -> Result<Vec<Emoji>, ClientError>
pub async fn get_guild_emojis( &self, guild_id: &str, ) -> Result<Vec<Emoji>, ClientError>
Returns all custom emojis in a guild.
Sourcepub async fn delete_guild_emoji(
&self,
guild_id: &str,
emoji_id: &str,
) -> Result<(), ClientError>
pub async fn delete_guild_emoji( &self, guild_id: &str, emoji_id: &str, ) -> Result<(), ClientError>
Deletes a custom emoji from a guild.
Sourcepub async fn get_channel_webhooks(
&self,
channel_id: &str,
) -> Result<Vec<Webhook>, ClientError>
pub async fn get_channel_webhooks( &self, channel_id: &str, ) -> Result<Vec<Webhook>, ClientError>
Returns all webhooks for a channel.
Sourcepub async fn get_guild_webhooks(
&self,
guild_id: &str,
) -> Result<Vec<Webhook>, ClientError>
pub async fn get_guild_webhooks( &self, guild_id: &str, ) -> Result<Vec<Webhook>, ClientError>
Returns all webhooks for a guild.
Sourcepub async fn create_webhook(
&self,
channel_id: &str,
name: &str,
avatar: Option<&str>,
) -> Result<Webhook, ClientError>
pub async fn create_webhook( &self, channel_id: &str, name: &str, avatar: Option<&str>, ) -> Result<Webhook, ClientError>
avatar should be a data URI if provided.
Sourcepub async fn delete_webhook(&self, webhook_id: &str) -> Result<(), ClientError>
pub async fn delete_webhook(&self, webhook_id: &str) -> Result<(), ClientError>
Deletes a webhook.
Sourcepub async fn send_files(
&self,
channel_id: &str,
files: Vec<AttachmentFile>,
content: Option<&str>,
) -> Result<Message, ClientError>
pub async fn send_files( &self, channel_id: &str, files: Vec<AttachmentFile>, content: Option<&str>, ) -> Result<Message, ClientError>
Sends a message with one or more file attachments. content is optional
if you just want to upload files with no text.
Examples found in repository?
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn send_message_with_files(
&self,
channel_id: &str,
payload: &MessageCreatePayload,
files: Vec<AttachmentFile>,
) -> Result<Message, ClientError>
pub async fn send_message_with_files( &self, channel_id: &str, payload: &MessageCreatePayload, files: Vec<AttachmentFile>, ) -> Result<Message, ClientError>
Like send_message_advanced but also uploads files as attachments.
Sourcepub async fn search_messages(
&self,
query: &SearchMessagesQuery,
) -> Result<SearchMessagesResponse, ClientError>
pub async fn search_messages( &self, query: &SearchMessagesQuery, ) -> Result<SearchMessagesResponse, ClientError>
Searches messages across the platform. See SearchMessagesQuery for
all available filters (content, author, channel, attachments, etc).
Sourcepub async fn ack_bulk(
&self,
states: Vec<ReadStateAck>,
) -> Result<(), ClientError>
pub async fn ack_bulk( &self, states: Vec<ReadStateAck>, ) -> Result<(), ClientError>
Marks multiple channels as read in a single request. Pass up to 100
(channel_id, last_message_id) pairs.
Sourcepub async fn execute_webhook(
&self,
webhook_id: &str,
webhook_token: &str,
payload: &WebhookExecutePayload,
) -> Result<Option<Message>, ClientError>
pub async fn execute_webhook( &self, webhook_id: &str, webhook_token: &str, payload: &WebhookExecutePayload, ) -> Result<Option<Message>, ClientError>
Executes a webhook (sends a message through it). Uses wait=true so
the response includes the full message object.
Sourcepub async fn get_webhook_message(
&self,
webhook_id: &str,
webhook_token: &str,
message_id: &str,
) -> Result<Message, ClientError>
pub async fn get_webhook_message( &self, webhook_id: &str, webhook_token: &str, message_id: &str, ) -> Result<Message, ClientError>
Gets a message previously sent by a webhook.
Sourcepub async fn edit_webhook_message(
&self,
webhook_id: &str,
webhook_token: &str,
message_id: &str,
payload: &WebhookEditPayload,
) -> Result<Message, ClientError>
pub async fn edit_webhook_message( &self, webhook_id: &str, webhook_token: &str, message_id: &str, payload: &WebhookEditPayload, ) -> Result<Message, ClientError>
Edits a message previously sent by a webhook.