discord-authority 0.1.2

A fast, async Discord selfbot library for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
use crate::{Error, Result};
use crate::events::{EventHandler, DefaultEventHandler};
use crate::gateway::Gateway;
use crate::http::HttpClient;
use crate::models::{User, Message, Activity, Channel, Guild};
use crate::models::guild::{GuildMember, Role, Ban};
use crate::models::channel::Invite;
use crate::models::message::MessageBuilder;
use crate::models::embed::Embed;
use crate::utils::Snowflake;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::info;

pub struct Client {
    gateway: Arc<Gateway>,
    pub http: Arc<HttpClient>,
    user: Arc<RwLock<Option<User>>>,
}

impl Client {
    // ── Self ──────────────────────────────────────────────────────────────────

    /// Returns the current authenticated user.
    pub async fn current_user(&self) -> Option<User> {
        self.user.read().await.clone()
    }

    /// Refreshes and returns the current user from the API.
    pub async fn fetch_me(&self) -> Result<User> {
        self.http.get_me().await
    }

    // ── Messages ──────────────────────────────────────────────────────────────

    /// Send a plain text message to a channel.
    pub async fn send_message(&self, channel_id: Snowflake, content: impl Into<String>) -> Result<Message> {
        self.http.send_message(channel_id, serde_json::json!({ "content": content.into() })).await
    }

    /// Send a message using a `MessageBuilder` payload.
    pub async fn send(&self, channel_id: Snowflake, builder: MessageBuilder) -> Result<Message> {
        self.http.send_message(channel_id, builder.build()).await
    }

    /// Send an embed to a channel.
    pub async fn send_embed(&self, channel_id: Snowflake, embed: Embed) -> Result<Message> {
        let payload = MessageBuilder::new().embed(embed).build();
        self.http.send_message(channel_id, payload).await
    }

    /// Send a message with content and an embed.
    pub async fn send_message_with_embed(
        &self,
        channel_id: Snowflake,
        content: impl Into<String>,
        embed: Embed,
    ) -> Result<Message> {
        let payload = MessageBuilder::new().content(content.into()).embed(embed).build();
        self.http.send_message(channel_id, payload).await
    }

    /// Edit a message's content.
    pub async fn edit_message(
        &self,
        channel_id: Snowflake,
        message_id: Snowflake,
        content: impl Into<String>,
    ) -> Result<Message> {
        self.http.edit_message(channel_id, message_id, serde_json::json!({ "content": content.into() })).await
    }

    /// Delete a message.
    pub async fn delete_message(&self, channel_id: Snowflake, message_id: Snowflake) -> Result<()> {
        self.http.delete_message(channel_id, message_id).await
    }

    /// Bulk delete messages (2–100, must be under 14 days old).
    pub async fn bulk_delete_messages(&self, channel_id: Snowflake, message_ids: Vec<Snowflake>) -> Result<()> {
        self.http.bulk_delete_messages(channel_id, message_ids).await
    }

    /// Fetch a single message.
    pub async fn get_message(&self, channel_id: Snowflake, message_id: Snowflake) -> Result<Message> {
        self.http.get_message(channel_id, message_id).await
    }

    /// Fetch message history from a channel.
    pub async fn get_messages(
        &self,
        channel_id: Snowflake,
        limit: u8,
        before: Option<Snowflake>,
        after: Option<Snowflake>,
    ) -> Result<Vec<Message>> {
        self.http.get_messages(channel_id, limit, before, after, None).await
    }

    /// Pin a message in a channel.
    pub async fn pin_message(&self, channel_id: Snowflake, message_id: Snowflake) -> Result<()> {
        self.http.pin_message(channel_id, message_id).await
    }

    /// Unpin a message from a channel.
    pub async fn unpin_message(&self, channel_id: Snowflake, message_id: Snowflake) -> Result<()> {
        self.http.unpin_message(channel_id, message_id).await
    }

    /// Fetch pinned messages in a channel.
    pub async fn get_pinned_messages(&self, channel_id: Snowflake) -> Result<Vec<Message>> {
        self.http.get_pinned_messages(channel_id).await
    }

    // ── Reactions ─────────────────────────────────────────────────────────────

    /// Add a reaction to a message.
    pub async fn add_reaction(&self, channel_id: Snowflake, message_id: Snowflake, emoji: &str) -> Result<()> {
        self.http.add_reaction(channel_id, message_id, emoji).await
    }

    /// Remove your reaction from a message.
    pub async fn remove_reaction(&self, channel_id: Snowflake, message_id: Snowflake, emoji: &str) -> Result<()> {
        self.http.remove_reaction(channel_id, message_id, emoji).await
    }

    /// Remove all reactions from a message.
    pub async fn clear_reactions(&self, channel_id: Snowflake, message_id: Snowflake) -> Result<()> {
        self.http.clear_reactions(channel_id, message_id).await
    }

    // ── Channels ──────────────────────────────────────────────────────────────

    /// Fetch a channel by ID.
    pub async fn get_channel(&self, channel_id: Snowflake) -> Result<Channel> {
        self.http.get_channel(channel_id).await
    }

    /// Open or fetch a DM channel with a user.
    pub async fn create_dm(&self, user_id: Snowflake) -> Result<Channel> {
        self.http.create_dm(user_id).await
    }

    /// Send a DM to a user (opens the DM channel automatically).
    pub async fn send_dm(&self, user_id: Snowflake, content: impl Into<String>) -> Result<Message> {
        let channel = self.create_dm(user_id).await?;
        self.send_message(channel.id(), content).await
    }

    /// Start the typing indicator in a channel.
    pub async fn start_typing(&self, channel_id: Snowflake) -> Result<()> {
        self.http.start_typing(channel_id).await
    }

    /// Create an invite for a channel.
    pub async fn create_invite(
        &self,
        channel_id: Snowflake,
        max_age: u32,
        max_uses: u32,
        temporary: bool,
    ) -> Result<Invite> {
        self.http.create_invite(channel_id, max_age, max_uses, temporary).await
    }

    // ── Polls ─────────────────────────────────────────────────────────────────

    /// Vote on a poll answer.
    pub async fn vote_poll(&self, channel_id: Snowflake, message_id: Snowflake, answer_id: u32) -> Result<()> {
        self.http.vote_poll(channel_id, message_id, answer_id).await
    }

    /// Remove your vote from a poll answer.
    pub async fn unvote_poll(&self, channel_id: Snowflake, message_id: Snowflake, answer_id: u32) -> Result<()> {
        self.http.unvote_poll(channel_id, message_id, answer_id).await
    }

    // ── Guilds ────────────────────────────────────────────────────────────────

    /// Fetch a guild by ID.
    pub async fn get_guild(&self, guild_id: Snowflake) -> Result<Guild> {
        self.http.get_guild(guild_id).await
    }

    /// Fetch all guilds the current user is in.
    pub async fn get_my_guilds(&self) -> Result<Vec<serde_json::Value>> {
        self.http.get_my_guilds().await
    }

    /// Leave a guild.
    pub async fn leave_guild(&self, guild_id: Snowflake) -> Result<()> {
        self.http.leave_guild(guild_id).await
    }

    // ── Members ───────────────────────────────────────────────────────────────

    /// Fetch a guild member.
    pub async fn get_member(&self, guild_id: Snowflake, user_id: Snowflake) -> Result<GuildMember> {
        self.http.get_member(guild_id, user_id).await
    }

    /// Kick a member from a guild.
    pub async fn kick_member(&self, guild_id: Snowflake, user_id: Snowflake) -> Result<()> {
        self.http.kick_member(guild_id, user_id).await
    }

    /// Ban a member from a guild.
    pub async fn ban_member(
        &self,
        guild_id: Snowflake,
        user_id: Snowflake,
        delete_message_seconds: u32,
        reason: Option<&str>,
    ) -> Result<()> {
        self.http.ban_member(guild_id, user_id, delete_message_seconds, reason).await
    }

    /// Unban a user from a guild.
    pub async fn unban_member(&self, guild_id: Snowflake, user_id: Snowflake) -> Result<()> {
        self.http.unban_member(guild_id, user_id).await
    }

    /// Fetch the ban list for a guild.
    pub async fn get_bans(&self, guild_id: Snowflake) -> Result<Vec<Ban>> {
        self.http.get_bans(guild_id).await
    }

    /// Add a role to a guild member.
    pub async fn add_role(&self, guild_id: Snowflake, user_id: Snowflake, role_id: Snowflake) -> Result<()> {
        self.http.add_member_role(guild_id, user_id, role_id).await
    }

    /// Remove a role from a guild member.
    pub async fn remove_role(&self, guild_id: Snowflake, user_id: Snowflake, role_id: Snowflake) -> Result<()> {
        self.http.remove_member_role(guild_id, user_id, role_id).await
    }

    /// Set a member's nickname. Pass `None` to reset it.
    pub async fn set_nickname(&self, guild_id: Snowflake, user_id: Snowflake, nick: Option<&str>) -> Result<()> {
        self.http.set_nickname(guild_id, user_id, nick).await
    }

    // ── Roles ─────────────────────────────────────────────────────────────────

    /// Fetch all roles in a guild.
    pub async fn get_roles(&self, guild_id: Snowflake) -> Result<Vec<Role>> {
        self.http.get_roles(guild_id).await
    }

    /// Delete a role from a guild.
    pub async fn delete_role(&self, guild_id: Snowflake, role_id: Snowflake) -> Result<()> {
        self.http.delete_role(guild_id, role_id).await
    }

    // ── Users ─────────────────────────────────────────────────────────────────

    /// Fetch a user by ID.
    pub async fn get_user(&self, user_id: Snowflake) -> Result<User> {
        self.http.get_user(user_id).await
    }

    // ── Presence ──────────────────────────────────────────────────────────────

    /// Set presence with multiple activities and a status string.
    pub async fn set_presence(&self, activities: Vec<Activity>, status: &str) -> Result<()> {
        self.gateway.update_presence(activities, status).await
    }

    /// Set a single activity (keeps status as "online").
    pub async fn set_activity(&self, activity: Activity) -> Result<()> {
        self.gateway.update_presence(vec![activity], "online").await
    }

    /// Set status only (online, idle, dnd, invisible).
    pub async fn set_status(&self, status: &str) -> Result<()> {
        self.gateway.update_presence(vec![], status).await
    }

    /// Clear all activities.
    pub async fn clear_activity(&self) -> Result<()> {
        self.gateway.update_presence(vec![], "online").await
    }

    // ── Gateway ───────────────────────────────────────────────────────────────

    /// Start listening for gateway events. This blocks until the connection closes.
    pub async fn listen(&self) -> Result<()> {
        self.gateway.listen().await
    }
}

pub struct ClientBuilder {
    token: String,
    event_handler: Option<Arc<dyn EventHandler>>,
}

impl ClientBuilder {
    pub fn new(token: impl Into<String>) -> Self {
        Self {
            token: token.into(),
            event_handler: None,
        }
    }

    pub fn event_handler(mut self, handler: Arc<dyn EventHandler>) -> Self {
        self.event_handler = Some(handler);
        self
    }

    pub async fn build(self) -> Result<Client> {
        if std::env::var("RUST_LOG").is_err() {
            std::env::set_var("RUST_LOG", "info");
        }
        tracing_subscriber::fmt::init();

        let token = self.token;
        let event_handler = self.event_handler.unwrap_or_else(|| Arc::new(DefaultEventHandler));

        let http = Arc::new(HttpClient::new(token.clone()));

        info!("Verifying token...");
        let user = http.get_me().await.map_err(|_| Error::InvalidToken)?;
        info!("Authenticated as: {}", user.tag());

        let gateway = Arc::new(Gateway::new(token, event_handler));

        info!("Connecting to Discord Gateway...");
        gateway.connect().await?;

        Ok(Client {
            gateway,
            http,
            user: Arc::new(RwLock::new(Some(user))),
        })
    }
}

// ── Message helper methods ────────────────────────────────────────────────────

impl Message {
    /// Reply to this message.
    pub async fn reply(&self, http: &HttpClient, content: impl Into<String>) -> Result<Message> {
        let payload = serde_json::json!({
            "content": content.into(),
            "message_reference": {
                "message_id": self.id.to_string(),
                "channel_id": self.channel_id.to_string(),
            }
        });
        http.send_message(self.channel_id, payload).await
    }

    /// Reply with an embed.
    pub async fn reply_embed(&self, http: &HttpClient, embed: Embed) -> Result<Message> {
        let payload = MessageBuilder::new()
            .embed(embed)
            .reply_to(self.id)
            .build();
        http.send_message(self.channel_id, payload).await
    }

    /// Add a reaction to this message.
    pub async fn react(&self, http: &HttpClient, emoji: &str) -> Result<()> {
        http.add_reaction(self.channel_id, self.id, emoji).await
    }

    /// Delete this message.
    pub async fn delete(&self, http: &HttpClient) -> Result<()> {
        http.delete_message(self.channel_id, self.id).await
    }

    /// Edit this message's content.
    pub async fn edit(&self, http: &HttpClient, content: impl Into<String>) -> Result<Message> {
        http.edit_message(self.channel_id, self.id, serde_json::json!({ "content": content.into() })).await
    }

    /// Pin this message.
    pub async fn pin(&self, http: &HttpClient) -> Result<()> {
        http.pin_message(self.channel_id, self.id).await
    }

    /// Unpin this message.
    pub async fn unpin(&self, http: &HttpClient) -> Result<()> {
        http.unpin_message(self.channel_id, self.id).await
    }

    /// Returns true if this message was sent by a bot.
    pub fn is_from_bot(&self) -> bool {
        self.author.bot
    }

    /// Returns true if this message mentions everyone.
    pub fn mentions_everyone(&self) -> bool {
        self.mention_everyone
    }

    /// Returns true if this message has any embeds.
    pub fn has_embeds(&self) -> bool {
        !self.embeds.is_empty()
    }

    /// Returns true if this message has any attachments.
    pub fn has_attachments(&self) -> bool {
        !self.attachments.is_empty()
    }
}