1use fluxer::prelude::*;
2use async_trait::async_trait;
3use std::time::Instant;
4
5const PREFIX: &str = "!";
6
7struct Handler;
8
9fn parse_command(content: &str) -> Option<(&str, &str)> {
10 let trimmed = content.strip_prefix(PREFIX)?;
11 match trimmed.find(' ') {
12 Some(pos) => Some((&trimmed[..pos], trimmed[pos + 1..].trim())),
13 None => Some((trimmed, "")),
14 }
15}
16
17#[async_trait]
18impl EventHandler for Handler {
19 async fn on_ready(&self, ctx: Context, ready: Ready) {
20 println!("Logged in as {}", ready.user.username);
21 println!("current_user from cache: {:?}", ctx.cache.current_user().await);
22 }
23
24 async fn on_guild_create(&self, ctx: Context, guild: Guild) {
25 println!("GUILD_CREATE: {} (cache size: {})", guild.id, ctx.cache.guild_count().await);
26 }
27
28 async fn on_message(&self, ctx: Context, msg: Message) {
29 let user_cached = ctx.cache.user(&msg.author.id).await.is_some();
30 let ch_cached = ctx.cache.channel(msg.channel_id.as_deref().unwrap_or("")).await.is_some();
31 let content_preview = msg.content.as_deref().unwrap_or("").chars().take(60).collect::<String>();
32 let attachments = msg.attachments.as_ref().map(|a| a.len()).unwrap_or(0);
33 let embeds = msg.embeds.as_ref().map(|e| e.len()).unwrap_or(0);
34 println!(
35 "[msg] author={}#{} channel={} guild={} | \"{}\" | attach={} embeds={} | cache: user={} ch={}",
36 msg.author.username,
37 msg.author.discriminator.as_deref().unwrap_or("0"),
38 msg.channel_id.as_deref().unwrap_or("?"),
39 msg.guild_id.as_deref().unwrap_or("DM"),
40 content_preview,
41 attachments,
42 embeds,
43 user_cached,
44 ch_cached,
45 );
46
47 if msg.author.bot.unwrap_or(false) {
48 return;
49 }
50
51 let content = match msg.content.as_deref() {
52 Some(c) => c,
53 None => return,
54 };
55
56 let channel_id = msg.channel_id.as_deref().unwrap_or_default();
57
58 let (cmd, args) = match parse_command(content) {
59 Some(v) => v,
60 None => return,
61 };
62
63 match cmd {
64 "ping" => {
65 let start = Instant::now();
66 let sent = ctx.http.send_message(channel_id, "Pong!").await;
67 let elapsed = start.elapsed().as_millis();
68
69 if let Ok(sent) = sent {
70 let _ = ctx.http.edit_message(
71 channel_id,
72 &sent.id,
73 &format!("Pong! {}ms", elapsed),
74 ).await;
75 }
76 }
77
78 "say" => {
79 if args.is_empty() {
80 let _ = ctx.http.send_message(channel_id, "Say what?").await;
81 return;
82 }
83 let _ = ctx.http.delete_message(channel_id, &msg.id).await;
84 let _ = ctx.http.send_message(channel_id, args).await;
85 }
86
87 "embed" => {
88 let (title, desc) = match args.split_once('|') {
89 Some((t, d)) => (t.trim(), d.trim()),
90 None => {
91 let _ = ctx.http.send_message(channel_id, "`!embed title | description`").await;
92 return;
93 }
94 };
95
96 let embed = EmbedBuilder::new()
97 .title(title)
98 .description(desc)
99 .color(0x5865F2)
100 .build();
101
102 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
103 }
104
105 "react" => {
106 let _ = ctx.http.add_reaction(channel_id, &msg.id, "❤️").await;
107 }
108
109 "purge" => {
110 let count: u8 = args.parse().unwrap_or(0);
111 if count == 0 || count > 100 {
112 let _ = ctx.http.send_message(channel_id, "1-100.").await;
113 return;
114 }
115
116 let query = GetMessagesQuery {
117 limit: Some(count),
118 ..Default::default()
119 };
120
121 if let Ok(messages) = ctx.http.get_messages(channel_id, query).await {
122 let ids: Vec<&str> = messages.iter().map(|m| m.id.as_str()).collect();
123 let _ = ctx.http.bulk_delete_messages(channel_id, ids).await;
124 }
125 }
126
127 "serverinfo" => {
128 let guild_id = match &msg.guild_id {
129 Some(id) => id.as_str(),
130 None => return,
131 };
132
133 if let Ok(guild) = ctx.http.get_guild(guild_id).await {
134 let name = guild.name.as_deref().unwrap_or("Unknown");
135
136 let members = ctx.http.get_guild_members(guild_id, Some(1000), None).await
137 .map(|m| m.len().to_string())
138 .unwrap_or("?".into());
139
140 let embed = EmbedBuilder::new()
141 .title(name)
142 .field("Members", &members, true)
143 .color(0x5865F2)
144 .build();
145
146 let _ = ctx.http.send_embed(channel_id, None, vec![embed]).await;
147 }
148 }
149
150 _ => {}
151 }
152 }
153}
154
155#[tokio::main]
156async fn main() {
157 rustls::crypto::ring::default_provider()
158 .install_default()
159 .expect("Failed to install rustls crypto provider");
160
161 let token = std::env::var("FLUXER_TOKEN")
162 .expect("Set FLUXER_TOKEN to your bot token");
163
164 let mut client = Client::builder(&token)
165 .event_handler(Handler)
167 .build();
168
169 if let Err(e) = client.start().await {
170 eprintln!("Error: {}", e);
171 }
172}