Skip to main content

Http

Struct Http 

Source
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: String

Implementations§

Source§

impl Http

Source

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.

Source

pub fn new_user(token: &str, base_url: String) -> Self

Creates a new HTTP client for a user token (no Bot prefix).

Source

pub fn get_token(&self) -> &str

Source

pub async fn get_gateway(&self) -> Result<String, ClientError>

Fetches the gateway URL. Used internally during connection setup.

Source

pub async fn get_well_known_fluxer( &self, ) -> Result<WellKnownFluxerResponse, ClientError>

Returns the instance discovery document for the current base URL.

Source

pub async fn get_me(&self) -> Result<User, ClientError>

Fetches the bot’s own user object.

Source

pub async fn get_user(&self, user_id: &str) -> Result<User, ClientError>

Fetches a user by ID.

Source

pub async fn get_current_user_guilds(&self) -> Result<Vec<Guild>, ClientError>

Returns all guilds the bot is in.

Source

pub async fn get_channel( &self, channel_id: &str, ) -> Result<Channel, ClientError>

Fetches a channel by ID.

Source

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.

Source

pub async fn delete_channel(&self, channel_id: &str) -> Result<(), ClientError>

Permanently deletes a channel. Can’t be undone.

Source

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)

Source

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?
examples/selfbot.rs (line 107)
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, 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().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
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
Hide additional examples
examples/bot.rs (line 120)
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    }
Source

pub async fn get_message( &self, channel_id: &str, message_id: &str, ) -> Result<Message, ClientError>

Fetches a single message by ID.

Source

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?
examples/voice.rs (line 51)
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
Hide additional examples
examples/selfbot.rs (line 48)
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, 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().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
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    }
examples/bot.rs (line 65)
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    }
Source

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).

Source

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?
examples/selfbot.rs (line 134)
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, 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().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
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
Hide additional examples
examples/bot.rs (line 101)
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    }
Source

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.

Source

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?
examples/selfbot.rs (lines 51-55)
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, 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().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
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
Hide additional examples
examples/bot.rs (lines 69-73)
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    }
Source

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.

Source

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?
examples/selfbot.rs (line 133)
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, 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().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
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
Hide additional examples
examples/bot.rs (line 82)
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    }
Source

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?
examples/selfbot.rs (line 116)
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, 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().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
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
Hide additional examples
examples/bot.rs (line 122)
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    }
Source

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?
examples/bot.rs (line 105)
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    }
Source

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.

Source

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.

Source

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.

Source

pub async fn clear_reactions( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>

Removes all reactions from a message. Needs Manage Messages.

Source

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.

Source

pub async fn get_pins( &self, channel_id: &str, ) -> Result<PinsResponse, ClientError>

Fetches pinned messages in a channel.

Source

pub async fn pin_message( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>

Pins a message. Needs Manage Messages.

Source

pub async fn unpin_message( &self, channel_id: &str, message_id: &str, ) -> Result<(), ClientError>

Unpins a message. Needs Manage Messages.

Source

pub async fn get_invite(&self, invite_code: &str) -> Result<Invite, ClientError>

Fetches an invite by code. Includes approximate member counts.

Source

pub async fn create_invite( &self, channel_id: &str, payload: &CreateInvitePayload, ) -> Result<Invite, ClientError>

Creates an invite for a channel.

Source

pub async fn delete_invite(&self, invite_code: &str) -> Result<(), ClientError>

Deletes an invite by code.

Source

pub async fn accept_invite( &self, invite_code: &str, ) -> Result<Invite, ClientError>

Accepts an invite.

Source

pub async fn get_channel_invites( &self, channel_id: &str, ) -> Result<Vec<Invite>, ClientError>

Returns all active invites for a channel.

Source

pub async fn get_guild_invites( &self, guild_id: &str, ) -> Result<Vec<Invite>, ClientError>

Returns all active invites for a guild.

Source

pub async fn get_guild(&self, guild_id: &str) -> Result<Guild, ClientError>

Fetches a guild by ID.

Examples found in repository?
examples/bot.rs (line 132)
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    }
Source

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.

Source

pub async fn delete_guild(&self, guild_id: &str) -> Result<(), ClientError>

Permanently deletes a guild. The bot must be the owner. (not tested)

Source

pub async fn create_guild( &self, payload: &GuildCreatePayload, ) -> Result<Guild, ClientError>

Creates a guild.

Source

pub async fn get_guild_channels( &self, guild_id: &str, ) -> Result<Vec<Channel>, ClientError>

Returns all channels in a guild.

Source

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.

Source

pub async fn get_guild_member( &self, guild_id: &str, user_id: &str, ) -> Result<Member, ClientError>

Fetches a single guild member.

Source

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?
examples/bot.rs (line 135)
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    }
Source

pub async fn kick_member( &self, guild_id: &str, user_id: &str, ) -> Result<(), ClientError>

Kicks a member from the guild.

Source

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).

Source

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?
examples/bot.rs (line 202)
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    }
Source

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?
examples/bot.rs (line 218)
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    }
Source

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.

Source

pub async fn unban_member( &self, guild_id: &str, user_id: &str, ) -> Result<(), ClientError>

Removes a ban.

Source

pub async fn get_guild_bans( &self, guild_id: &str, ) -> Result<Vec<Value>, ClientError>

Returns the guild’s ban list.

Source

pub async fn get_guild_roles( &self, guild_id: &str, ) -> Result<Vec<Role>, ClientError>

Returns all roles in a guild.

Source

pub async fn create_role( &self, guild_id: &str, payload: &CreateRolePayload, ) -> Result<Role, ClientError>

Creates a new role in a guild.

Source

pub async fn edit_role( &self, guild_id: &str, role_id: &str, payload: &EditRolePayload, ) -> Result<Role, ClientError>

Edits a role’s properties.

Source

pub async fn delete_role( &self, guild_id: &str, role_id: &str, ) -> Result<(), ClientError>

Deletes a role.

Source

pub async fn get_guild_emojis( &self, guild_id: &str, ) -> Result<Vec<Emoji>, ClientError>

Returns all custom emojis in a guild.

Source

pub async fn get_emoji_metadata( &self, emoji_id: &str, ) -> Result<GuildEmojiMetadata, ClientError>

Returns metadata about an emoji.

Source

pub async fn delete_guild_emoji( &self, guild_id: &str, emoji_id: &str, ) -> Result<(), ClientError>

Deletes a custom emoji from a guild.

Source

pub async fn get_channel_webhooks( &self, channel_id: &str, ) -> Result<Vec<Webhook>, ClientError>

Returns all webhooks for a channel.

Source

pub async fn get_guild_webhooks( &self, guild_id: &str, ) -> Result<Vec<Webhook>, ClientError>

Returns all webhooks for a guild.

Source

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.

Source

pub async fn delete_webhook(&self, webhook_id: &str) -> Result<(), ClientError>

Deletes a webhook.

Source

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?
examples/bot.rs (line 180)
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    }
Source

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.

Source

pub async fn get_rtc_regions( &self, channel_id: &str, ) -> Result<Vec<RtcRegion>, ClientError>

Lists RTC regions available for a voice channel.

Source

pub async fn get_channel_slowmode( &self, channel_id: &str, ) -> Result<ChannelSlowmodeState, ClientError>

Returns the current slowmode state for the caller in a channel.

Source

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).

Source

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.

Source

pub async fn get_guild_audit_logs( &self, guild_id: &str, ) -> Result<GuildAuditLogList, ClientError>

Returns the guild audit log and referenced users/webhooks.

Source

pub async fn get_guild_vanity_url( &self, guild_id: &str, ) -> Result<GuildVanityUrl, ClientError>

Returns the guild vanity invite code and usage count.

Source

pub async fn update_guild_vanity_url( &self, guild_id: &str, payload: &GuildVanityUrlUpdatePayload, ) -> Result<GuildVanityUrlUpdateResponse, ClientError>

Sets or clears the guild vanity invite code.

Source

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.

Source

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.

Source

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.

Auto Trait Implementations§

§

impl !RefUnwindSafe for Http

§

impl !UnwindSafe for Http

§

impl Freeze for Http

§

impl Send for Http

§

impl Sync for Http

§

impl Unpin for Http

§

impl UnsafeUnpin for Http

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more