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 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_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/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            _ => {}
193        }
194    }
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/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            _ => {}
193        }
194    }
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/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            _ => {}
193        }
194    }
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/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            _ => {}
193        }
194    }
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/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            _ => {}
193        }
194    }
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/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            _ => {}
193        }
194    }
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            _ => {}
193        }
194    }
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 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            _ => {}
193        }
194    }
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 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            _ => {}
193        }
194    }
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 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 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            _ => {}
193        }
194    }
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 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.

Auto Trait Implementations§

§

impl Freeze for Http

§

impl !RefUnwindSafe for Http

§

impl Send for Http

§

impl Sync for Http

§

impl Unpin for Http

§

impl UnsafeUnpin for Http

§

impl !UnwindSafe 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<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: 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: 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> 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