Skip to main content

selfbot/
selfbot.rs

1use fluxer::prelude::*;
2use std::time::Instant;
3
4const PREFIX: &str = "sb!";
5
6struct Handler;
7
8fn parse_command(content: &str) -> Option<(&str, &str)> {
9    let trimmed = content.strip_prefix(PREFIX)?;
10    match trimmed.find(' ') {
11        Some(pos) => Some((&trimmed[..pos], trimmed[pos + 1..].trim())),
12        None => Some((trimmed, "")),
13    }
14}
15
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, answer).await;
76            }
77
78            "flip" => {
79                let result = if std::time::SystemTime::now()
80                    .duration_since(std::time::UNIX_EPOCH)
81                    .unwrap()
82                    .as_nanos().is_multiple_of(2) { "Heads" } else { "Tails" };
83                let _ = ctx.http.edit_message(channel_id, &msg.id, result).await;
84            }
85
86            "roll" => {
87                let sides: u32 = args.parse().unwrap_or(6);
88                if !(2..=100).contains(&sides) {
89                    let _ = ctx.http.edit_message(channel_id, &msg.id, "Use 2-100 sides").await;
90                    return;
91                }
92                let result = (std::time::SystemTime::now()
93                    .duration_since(std::time::UNIX_EPOCH)
94                    .unwrap()
95                    .as_nanos() % sides as u128) as u32 + 1;
96                let _ = ctx.http.edit_message(channel_id, &msg.id, &format!("Rolled {}/{}", result, sides)).await;
97            }
98
99            "purge" => {
100                let count: u8 = args.parse().unwrap_or(10).min(100);
101                let query = GetMessagesQuery {
102                    limit: Some(count),
103                    before: None,
104                    after: None,
105                    around: None,
106                };
107                match ctx.http.get_messages(channel_id, query).await {
108                    Ok(messages) => {
109                        let my_id = ctx.cache.current_user().await.map(|u| u.id).unwrap_or_default();
110                        let my_messages: Vec<_> = messages.iter()
111                            .filter(|m| m.author.id == my_id)
112                            .map(|m| m.id.as_str())
113                            .collect();
114
115                        if !my_messages.is_empty() {
116                            let _ = ctx.http.bulk_delete_messages(channel_id, my_messages).await;
117                        }
118                    }
119                    Err(e) => eprintln!("[purge] failed: {}", e),
120                }
121            }
122
123            "embed" => {
124                if args.is_empty() {
125                    let _ = ctx.http.edit_message(channel_id, &msg.id, "Usage: sb!embed <text>").await;
126                    return;
127                }
128                let embed = EmbedBuilder::new()
129                    .description(args)
130                    .color(0x5865F2)
131                    .build();
132
133                let _ = ctx.http.delete_message(channel_id, &msg.id).await;
134                let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
135            }
136
137            "edit" => {
138                if args.is_empty() { return; }
139                let _ = ctx.http.edit_message(channel_id, &msg.id, args).await;
140            }
141
142            "help" => {
143                let help_text = "**Selfbot Commands**\n\
144                    `sb!ping` - Check latency\n\
145                    `sb!8ball <question>` - Ask the magic 8ball\n\
146                    `sb!flip` - Flip a coin\n\
147                    `sb!roll [sides]` - Roll a die (default 6)\n\
148                    `sb!purge [count]` - Delete your recent messages\n\
149                    `sb!embed <text>` - Send an embed\n\
150                    `sb!edit <text>` - Edit your message\n\
151                    `sb!help` - Show this message";
152                let _ = ctx.http.edit_message(channel_id, &msg.id, help_text).await;
153            }
154
155            _ => {}
156        }
157    }
158}
159
160#[tokio::main]
161async fn main() {
162    let token = std::env::var("FLUXER_TOKEN")
163        .expect("Set FLUXER_TOKEN to your user token");
164
165    let mut client = Client::builder(&token)
166        .user_token()
167        .event_handler(Handler)
168        .build();
169
170    if let Err(e) = client.start().await {
171        eprintln!("Error: {}", e);
172    }
173}