titanium_rs/
client.rs

1use crate::framework::Framework;
2use std::sync::Arc;
3use titanium_cache::{Cache, InMemoryCache};
4use titanium_gateway::{Event, Shard, ShardConfig};
5use titanium_http::HttpClient;
6use titanium_model::Intents;
7
8/// The main Titanium Client.
9///
10/// This struct holds the connection to Discord, including the Gateway Shard(s)
11/// and the HTTP client. It is the main entry point for your bot.
12#[derive(Clone)]
13pub struct Client {
14    pub shard: Arc<Shard>, // Wrapped in Arc for concurrent access/spawning
15    pub http: Arc<HttpClient>,
16    pub cache: Arc<InMemoryCache>,
17    pub framework: Option<Arc<Framework>>,
18    pub event_handler: Option<Arc<dyn EventHandler>>,
19    #[allow(dead_code)]
20    token: String,
21}
22
23impl Client {
24    /// Create a new Client Builder.
25    #[inline]
26    pub fn builder(token: impl Into<String>) -> ClientBuilder {
27        ClientBuilder::new(token)
28    }
29
30    /// Start the client and return the event stream.
31    ///
32    /// This spawns the shard connection in the background.
33    pub async fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
34        let (tx, rx) = flume::unbounded();
35
36        let shard = self.shard.clone();
37        let cache = self.cache.clone();
38        let http = self.http.clone();
39        let event_handler = self.event_handler.clone();
40
41        // Spawn shard loop
42        let tx_clone = tx.clone();
43        tokio::spawn(async move {
44            if let Err(e) = shard.run(tx_clone).await {
45                eprintln!("Shard error: {:?}", e);
46            }
47        });
48
49        // Event Processing Loop
50        while let Ok(event) = rx.recv_async().await {
51            // Update Cache
52            match &event {
53                Event::Ready(ready) => {
54                    cache.insert_user(ready.user.clone());
55                    // ... other cache updates
56                }
57                Event::GuildCreate(guild) => {
58                    cache.insert_guild(*guild.clone());
59                }
60                // ...
61                _ => {}
62            }
63
64            // Dispatch to Event Handler
65            if let Some(handler) = &event_handler {
66                match event {
67                    Event::Ready(ready) => {
68                        let ctx =
69                            Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
70                        handler.ready(ctx, *ready).await;
71                    }
72                    Event::MessageCreate(msg) => {
73                        let ctx =
74                            Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
75                        handler.message_create(ctx, *msg).await;
76                    }
77                    Event::InteractionCreate(interaction) => {
78                        let interaction_val = *interaction;
79                        let ctx = Context::new(
80                            http.clone(),
81                            cache.clone(),
82                            self.shard.clone(),
83                            Some(interaction_val.clone()),
84                        );
85                        handler.interaction_create(ctx, interaction_val).await;
86                    }
87                    Event::MessageReactionAdd(ev) => {
88                         let ctx = Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
89                         handler.reaction_add(ctx, *ev).await;
90                    }
91                    Event::ThreadCreate(thread) => {
92                         let ctx = Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
93                         handler.thread_create(ctx, *thread).await;
94                    }
95                     Event::GuildRoleCreate(ev) => {
96                        let ctx = Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
97                        handler.role_create(ctx, *ev).await;
98                    }
99                    _ => {}
100                }
101            }
102        }
103
104        Ok(())
105    }
106}
107
108use crate::prelude::*;
109use async_trait::async_trait;
110
111#[async_trait]
112pub trait EventHandler: Send + Sync {
113    async fn ready(&self, _ctx: Context, _ready: ReadyEventData<'_>) {}
114    async fn message_create(&self, _ctx: Context, _msg: Message<'_>) {}
115    async fn interaction_create(&self, _ctx: Context, _interaction: Interaction<'_>) {}
116    async fn reaction_add(&self, _ctx: Context, _add: titanium_model::MessageReactionAddEvent<'async_trait>) {}
117    async fn thread_create(&self, _ctx: Context, _thread: Channel<'async_trait>) {}
118    async fn role_create(&self, _ctx: Context, _role: GuildRoleEvent<'async_trait>) {}
119}
120
121pub struct ClientBuilder {
122    token: String,
123    intents: Intents,
124    framework: Option<Framework>,
125    event_handler: Option<Arc<dyn EventHandler>>,
126}
127
128impl ClientBuilder {
129    #[inline]
130    pub fn new(token: impl Into<String>) -> Self {
131        Self {
132            token: token.into(),
133            intents: Intents::default(),
134            framework: None,
135            event_handler: None,
136        }
137    }
138
139    pub fn intents(mut self, intents: Intents) -> Self {
140        self.intents = intents;
141        self
142    }
143
144    pub fn framework(mut self, framework: Framework) -> Self {
145        self.framework = Some(framework);
146        self
147    }
148
149    pub fn event_handler<H: EventHandler + 'static>(mut self, handler: H) -> Self {
150        self.event_handler = Some(Arc::new(handler));
151        self
152    }
153
154    pub async fn build(self) -> Result<Client, Box<dyn std::error::Error>> {
155        let http = Arc::new(HttpClient::new(self.token.clone())?);
156        let cache = Arc::new(titanium_cache::InMemoryCache::new());
157
158        let config = ShardConfig::new(self.token.clone(), self.intents);
159        let shard = Arc::new(Shard::new(0, 1, config));
160
161        Ok(Client {
162            shard,
163            http,
164            cache,
165            framework: self.framework.map(Arc::new),
166            event_handler: self.event_handler,
167            token: self.token,
168        })
169    }
170}