pub struct Cache {
pub guilds: RwLock<HashMap<Snowflake, Guild>>,
pub channels: RwLock<HashMap<Snowflake, Channel>>,
pub users: RwLock<HashMap<Snowflake, User>>,
pub current_user: RwLock<Option<User>>,
}Expand description
In-memory gateway cache.
Fields§
§guilds: RwLock<HashMap<Snowflake, Guild>>§channels: RwLock<HashMap<Snowflake, Channel>>§users: RwLock<HashMap<Snowflake, User>>§current_user: RwLock<Option<User>>Implementations§
Source§impl Cache
impl Cache
pub fn new() -> Arc<Self>
Sourcepub async fn guild(&self, id: &str) -> Option<Guild>
pub async fn guild(&self, id: &str) -> Option<Guild>
Looks up a guild by ID. Populated from GUILD_CREATE / GUILD_UPDATE events.
Sourcepub async fn channel(&self, id: &str) -> Option<Channel>
pub async fn channel(&self, id: &str) -> Option<Channel>
Looks up a channel by ID. Populated from GUILD_CREATE and CHANNEL_CREATE / CHANNEL_UPDATE events.
Examples found in repository?
examples/bot.rs (line 29)
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn user(&self, id: &str) -> Option<User>
pub async fn user(&self, id: &str) -> Option<User>
Looks up a user by ID. Populated from GUILD_CREATE member lists and MESSAGE_CREATE authors.
Examples found in repository?
examples/bot.rs (line 28)
27 async fn on_message(&self, ctx: Context, msg: Message) {
28 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
29 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
30 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
31 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
32 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
33 println!(
34 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
35 msg.author.username,
36 msg.author.discriminator.as_deref().unwrap_or("0"),
37 msg.channel_id.as_deref().unwrap_or("?"),
38 msg.guild_id.as_deref().unwrap_or("DM"),
39 content_preview,
40 attachments,
41 embeds,
42 user_cached,
43 ch_cached,
44 );
45
46 if msg.author.bot.unwrap_or(false) {
47 return;
48 }
49
50 let content = match msg.content.as_deref() {
51 Some(c) => c,
52 None => return,
53 };
54
55 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
56
57 let (cmd, args) = match parse_command(content) {
58 Some(v) => v,
59 None => return,
60 };
61
62 match cmd {
63 "ping" => {
64 let start = Instant::now();
65 let sent = ctx.http.send_message(channel_id, "Pong!").await;
66 let elapsed = start.elapsed().as_millis();
67
68 if let Ok(sent) = sent {
69 let _ = ctx.http.edit_message(
70 channel_id,
71 &sent.id,
72 &format!("Pong! {}ms", elapsed),
73 ).await;
74 }
75 }
76
77 "say" => {
78 if args.is_empty() {
79 let _ = ctx.http.send_message(channel_id, "Say what?").await;
80 return;
81 }
82 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
83 let _ = ctx.http.send_message(channel_id, args).await;
84 }
85
86 "embed" => {
87 let (title, desc) = match args.split_once('|') {
88 Some((t, d)) => (t.trim(), d.trim()),
89 None => {
90 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
91 return;
92 }
93 };
94
95 let embed = EmbedBuilder::new()
96 .title(title)
97 .description(desc)
98 .color(0x5865F2)
99 .build();
100
101 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
102 }
103
104 "react" => {
105 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
106 }
107
108 "purge" => {
109 let count: u8 = args.parse().unwrap_or(0);
110 if count == 0 || count > 100 {
111 let _ = ctx.http.send_message(channel_id, "1-100.").await;
112 return;
113 }
114
115 let query = GetMessagesQuery {
116 limit: Some(count),
117 ..Default::default()
118 };
119
120 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
121 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
122 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
123 }
124 }
125
126 "serverinfo" => {
127 let guild_id = match &msg.guild_id {
128 Some(id) => id.as_str(),
129 None => return,
130 };
131
132 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
133 let name = guild.name.as_deref().unwrap_or("Unknown");
134
135 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
136 .map(|m| m.len().to_string())
137 .unwrap_or("?".into());
138
139 let embed = EmbedBuilder::new()
140 .title(name)
141 .field("Members", &members, true)
142 .color(0x5865F2)
143 .build();
144
145 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
146 }
147 }
148
149 "attach" => {
150 if args.is_empty() {
151 let _ = ctx.http.send_message(channel_id, "Usage: `!attach <file path>`").await;
152 return;
153 }
154 let path = std::path::Path::new(args);
155 let filename = path
156 .file_name()
157 .and_then(|n| n.to_str())
158 .unwrap_or("file")
159 .to_string();
160 let content_type = match path.extension().and_then(|e| e.to_str()) {
161 Some("mp3") => "audio/mpeg",
162 Some("mp4") => "video/mp4",
163 Some("mov") => "video/quicktime",
164 Some("webm") => "video/webm",
165 Some("png") => "image/png",
166 Some("jpg") | Some("jpeg") => "image/jpeg",
167 Some("gif") => "image/gif",
168 Some("webp") => "image/webp",
169 Some("txt") => "text/plain",
170 Some("pdf") => "application/pdf",
171 _ => "application/octet-stream",
172 };
173 match tokio::fs::read(path).await {
174 Ok(data) => {
175 let file = AttachmentFile {
176 filename,
177 data,
178 content_type: Some(content_type.to_string()),
179 };
180 match ctx.http.send_files(channel_id, vec![file], None).await {
181 Ok(msg) => println!("[attach] sent message {}", msg.id),
182 Err(e) => eprintln!("[attach] error: {}", e),
183 }
184 }
185 Err(e) => {
186 eprintln!("[attach] failed to read file: {}", e);
187 let _ = ctx.http.send_message(channel_id, &format!("Failed to read file: {}", e)).await;
188 }
189 }
190 }
191
192 "addrole" => {
193 let guild_id = match &msg.guild_id {
194 Some(id) => id.as_str(),
195 None => return,
196 };
197 let parts: Vec<&str> = args.split_whitespace().collect();
198 if parts.len() != 2 {
199 let _ = ctx.http.send_message(channel_id, "Usage: `!addrole <user_id> <role_id>`").await;
200 return;
201 }
202 match ctx.http.add_member_role(guild_id, parts[0], parts[1]).await {
203 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role added").await; }
204 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
205 }
206 }
207
208 "removerole" => {
209 let guild_id = match &msg.guild_id {
210 Some(id) => id.as_str(),
211 None => return,
212 };
213 let parts: Vec<&str> = args.split_whitespace().collect();
214 if parts.len() != 2 {
215 let _ = ctx.http.send_message(channel_id, "Usage: `!removerole <user_id> <role_id>`").await;
216 return;
217 }
218 match ctx.http.remove_member_role(guild_id, parts[0], parts[1]).await {
219 Ok(_) => { let _ = ctx.http.send_message(channel_id, "Role removed").await; }
220 Err(e) => { let _ = ctx.http.send_message(channel_id, &format!("Error: {}", e)).await; }
221 }
222 }
223
224 _ => {}
225 }
226 }Sourcepub async fn current_user(&self) -> Option<User>
pub async fn current_user(&self) -> Option<User>
Returns the bot’s own User object, populated from the READY event.
Examples found in repository?
examples/selfbot.rs (line 17)
16async fn is_me(ctx: &Context, msg: &Message) -> bool {
17 ctx.cache.current_user().await
18 .map(|u| u.id == msg.author.id)
19 .unwrap_or(false)
20}
21
22#[async_trait]
23impl EventHandler for Handler {
24 async fn on_ready(&self, _ctx: Context, ready: Ready) {
25 println!("[ready] logged in as {}", ready.user.username);
26 }
27
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 if !is_me(&ctx, &msg).await {
30 return;
31 }
32
33 let content = match msg.content.as_deref() {
34 Some(c) => c,
35 None => return,
36 };
37
38 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
39
40 let (cmd, args) = match parse_command(content) {
41 Some(v) => v,
42 None => return,
43 };
44
45 match cmd {
46 "ping" => {
47 let start = Instant::now();
48 let sent = ctx.http.send_message(channel_id, "Pong!").await;
49 let elapsed = start.elapsed().as_millis();
50 if let Ok(sent) = sent {
51 let _ = ctx.http.edit_message(
52 channel_id,
53 &sent.id,
54 &format!("Pong! `{}ms`", elapsed),
55 ).await;
56 }
57 }
58
59 "8ball" => {
60 if args.is_empty() {
61 let _ = ctx.http.edit_message(channel_id, &msg.id, "Ask a question!").await;
62 return;
63 }
64 let responses = [
65 "Yes", "No", "Maybe", "Definitely", "Absolutely not",
66 "Ask again later", "Better not tell you now", "Cannot predict now",
67 "Don't count on it", "It is certain", "Most likely", "Outlook good",
68 "Reply hazy, try again", "Signs point to yes", "Very doubtful", "Without a doubt",
69 ];
70 let idx = (std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .unwrap()
73 .as_nanos() % responses.len() as u128) as usize;
74 let answer = responses[idx];
75 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", answer)).await;
76 }
77
78 "flip" => {
79 let result = if std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .unwrap()
82 .as_nanos() % 2 == 0 { "Heads" } else { "Tails" };
83 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("{}", result)).await;
84 }
85
86 "roll" => {
87 let sides: u32 = args.parse().unwrap_or(6);
88 if sides < 2 || sides > 100 {
89 let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90 return;
91 }
92 let result = (std::time::SystemTime::now()
93 .duration_since(std::time::UNIX_EPOCH)
94 .unwrap()
95 .as_nanos() % sides as u128) as u32 + 1;
96 let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97 }
98
99 "purge" => {
100 let count: u8 = args.parse().unwrap_or(10).min(100);
101 let query = GetMessagesQuery {
102 limit: Some(count),
103 before: None,
104 after: None,
105 around: None,
106 };
107 match ctx.http.get_messages(channel_id, query).await {
108 Ok(messages) => {
109 let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110 let my_messages: Vec<_> = messages.iter()
111 .filter(|m| m.author.id == my_id)
112 .map(|m| m.id.as_str())
113 .collect();
114
115 if !my_messages.is_empty() {
116 let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117 }
118 }
119 Err(e) => eprintln!("[purge] failed: {}", e),
120 }
121 }
122
123 "embed" => {
124 if args.is_empty() {
125 let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126 return;
127 }
128 let embed = EmbedBuilder::new()
129 .description(args)
130 .color(0x5865F2)
131 .build();
132
133 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135 }
136
137 "edit" => {
138 if args.is_empty() { return; }
139 let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140 }
141
142 "help" => {
143 let help_text = "**Selfbot Commands**\n\
144 `sb!ping` - Check latency\n\
145 `sb!8ball <question>` - Ask the magic 8ball\n\
146 `sb!flip` - Flip a coin\n\
147 `sb!roll [sides]` - Roll a die (default 6)\n\
148 `sb!purge [count]` - Delete your recent messages\n\
149 `sb!embed <text>` - Send an embed\n\
150 `sb!edit <text>` - Edit your message\n\
151 `sb!help` - Show this message";
152 let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153 }
154
155 _ => {}
156 }
157 }More examples
Sourcepub async fn guild_count(&self) -> usize
pub async fn guild_count(&self) -> usize
Returns the number of guilds currently in cache.
Auto Trait Implementations§
impl !Freeze for Cache
impl !RefUnwindSafe for Cache
impl Send for Cache
impl Sync for Cache
impl Unpin for Cache
impl UnsafeUnpin for Cache
impl !UnwindSafe for Cache
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more