summary refs log tree commit diff
path: root/src/system.rs
blob: e707d7f8515da3420e4aa2f997e47a2bf0d7e9c6 (plain)
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
use std::{num::NonZeroUsize, str::FromStr};

use std::sync::Arc;
use tokio::task::JoinSet;
use tokio::sync::Mutex;
use twilight_http::Client;
use twilight_model::id::{Id, marker::{MessageMarker, UserMarker}};
use twilight_gateway::{Intents, Shard, ShardId};

#[derive(Clone)]
pub struct System {
    pub config: crate::config::System,
    pub message_dedup_cache: Arc<Mutex<lru::LruCache<Id<MessageMarker>, ()>>>,
}

impl System {
    pub fn new(system_config: crate::config::System) -> Self {
        System {
            config: system_config,
            message_dedup_cache: Arc::new(Mutex::new(lru::LruCache::new(NonZeroUsize::new(100).unwrap())))
        }
    }

    pub async fn start_clients(&mut self) {
        println!("Starting clients for system");

        let reference_user_id : Id<UserMarker> = Id::from_str(self.config.reference_user_id.as_str())
            .expect(format!("Invalid user ID: {}", self.config.reference_user_id).as_str());

        let mut set = JoinSet::new();

        for member in self.config.members.iter() {
            let token = member.discord_token.clone();
            let self_clone = self.clone();
            set.spawn(async move {
                println!("Starting client with token: {}", token);

                let intents = Intents::GUILD_MEMBERS | Intents::GUILD_PRESENCES | Intents::GUILD_MESSAGES | Intents::MESSAGE_CONTENT;

                let mut shard = Shard::new(ShardId::ONE, token.clone(), intents);
                let mut client = Client::new(token.clone());

                loop {
                    let event = match shard.next_event().await {
                        Ok(event) => event,
                        Err(source) => {
                            println!("error receiving event");

                            if source.is_fatal() {
                                break;
                            }

                            continue;
                        }
                    };

                    match event {
                        twilight_gateway::Event::Ready(client) => {
                            println!("Bot started for {}#{}", client.user.name, client.user.discriminator);
                        },

                        twilight_gateway::Event::MessageCreate(message) => {
                            if message.author.id != reference_user_id {
                                continue
                            }

                            if self_clone.is_new_message(message.id).await {
                                self_clone.handle_message_create(message, &mut client).await;
                            }
                        },

                        twilight_gateway::Event::MessageUpdate(message) => {
                            if message.author.is_none() || message.author.as_ref().unwrap().id != reference_user_id {
                                continue
                            }

                            if self_clone.is_new_message(message.id).await {
                                self_clone.handle_message_update(message, &mut client).await;
                            }
                        },

                        _ => (),
                    }
                }
            });
        }

        while let Some(join_result) = set.join_next().await {
            if let Err(join_error) = join_result {
                println!("Task encountered error: {}", join_error);
            } else {
                println!("Task joined cleanly");
            }
        }
    }

    async fn is_new_message(&self, message_id: Id<MessageMarker>) -> bool {
        let mut message_cache = self.message_dedup_cache.lock().await;
        if let None = message_cache.get(&message_id) {
            message_cache.put(message_id, ());
            true
        } else {
            false
        }
    }

    async fn handle_message_create(&self, message: Box<twilight_model::gateway::payload::incoming::MessageCreate>, client: &mut Client) {
        println!("Message created: {}", message.content);

        if let Err(err) = client.create_message(message.channel_id)
            .reply(message.id)
            .content(&format!("Recognized message from authorized user {}", message.author.name)).expect("Error: reply too long")
            .await {
            println!("Error: {}", err);
        }
    }

    async fn handle_message_update(&self, _message: Box<twilight_model::gateway::payload::incoming::MessageUpdate>, _client: &mut Client) {
        // TODO: handle message edits and stuff
    }
}