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 Titan 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 = Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
69                        handler.ready(ctx, *ready).await;
70                    }
71                    Event::MessageCreate(msg) => {
72                        let ctx = Context::new(http.clone(), cache.clone(), self.shard.clone(), None);
73                        handler.message_create(ctx, *msg).await;
74                    }
75                    Event::InteractionCreate(interaction) => {
76                        let interaction_val = *interaction;
77                        let ctx = Context::new(
78                            http.clone(),
79                            cache.clone(),
80                            self.shard.clone(),
81                            Some(interaction_val.clone()),
82                        );
83                        handler.interaction_create(ctx, interaction_val).await;
84                    }
85                    _ => {}
86                }
87            }
88        }
89
90        Ok(())
91    }
92}
93
94
95use async_trait::async_trait;
96use crate::prelude::*;
97
98#[async_trait]
99pub trait EventHandler: Send + Sync {
100    async fn ready(&self, _ctx: Context, _ready: ReadyEventData<'_>) {}
101    async fn message_create(&self, _ctx: Context, _msg: Message<'_>) {}
102    async fn interaction_create(&self, _ctx: Context, _interaction: Interaction<'_>) {}
103    // Add other events as needed
104}
105
106pub struct ClientBuilder {
107    token: String,
108    intents: Intents,
109    framework: Option<Framework>,
110    event_handler: Option<Arc<dyn EventHandler>>,
111}
112
113impl ClientBuilder {
114    #[inline]
115    pub fn new(token: impl Into<String>) -> Self {
116        Self {
117            token: token.into(),
118            intents: Intents::default(),
119            framework: None,
120            event_handler: None,
121        }
122    }
123
124    pub fn intents(mut self, intents: Intents) -> Self {
125        self.intents = intents;
126        self
127    }
128
129    pub fn framework(mut self, framework: Framework) -> Self {
130        self.framework = Some(framework);
131        self
132    }
133
134    pub fn event_handler<H: EventHandler + 'static>(mut self, handler: H) -> Self {
135        self.event_handler = Some(Arc::new(handler));
136        self
137    }
138
139    pub async fn build(self) -> Result<Client, Box<dyn std::error::Error>> {
140        let http = Arc::new(HttpClient::new(self.token.clone())?);
141        let cache = Arc::new(titanium_cache::InMemoryCache::new());
142
143        let config = ShardConfig::new(self.token.clone(), self.intents);
144        let shard = Arc::new(Shard::new(0, 1, config));
145
146        Ok(Client {
147            shard,
148            http,
149            cache,
150            framework: self.framework.map(Arc::new),
151            event_handler: self.event_handler,
152            token: self.token,
153        })
154    }
155}
156