tulpje_framework/context/
command_context.rs

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
122
123
124
125
126
127
128
129
130
131
132
133
use std::sync::Arc;

use twilight_http::{client::InteractionClient, response::marker::EmptyBody, Client};
use twilight_model::{
    application::interaction::application_command::{CommandData, CommandOptionValue},
    channel::{message::MessageFlags, Message},
    gateway::payload::incoming::InteractionCreate,
    guild::Guild,
    http::interaction::{InteractionResponse, InteractionResponseType},
    id::{marker::ApplicationMarker, Id},
};
use twilight_util::builder::InteractionResponseDataBuilder;

use super::Context;
use crate::{Error, Metadata};

#[derive(Clone, Debug)]
pub struct CommandContext<T: Clone + Send + Sync> {
    pub meta: Metadata,
    pub application_id: Id<ApplicationMarker>,
    pub services: Arc<T>,
    pub client: Arc<Client>,

    pub event: InteractionCreate,
    pub command: CommandData,
}

impl<T: Clone + Send + Sync> CommandContext<T> {
    pub fn from_context(
        meta: Metadata,
        ctx: Context<T>,
        event: InteractionCreate,
        command: CommandData,
    ) -> Self {
        Self {
            meta,
            application_id: ctx.application_id,
            client: ctx.client,
            services: ctx.services,

            command,
            event,
        }
    }

    pub fn interaction(&self) -> InteractionClient<'_> {
        self.client.interaction(self.application_id)
    }

    pub fn client(&self) -> Arc<Client> {
        Arc::clone(&self.client)
    }

    pub async fn guild(&self) -> Result<Option<Guild>, Error> {
        let Some(guild_id) = self.event.guild_id else {
            return Ok(None);
        };

        Ok(Some(self.client.guild(guild_id).await?.model().await?))
    }

    pub async fn response(
        &self,
        response: InteractionResponse,
    ) -> Result<twilight_http::Response<EmptyBody>, twilight_http::Error> {
        self.interaction()
            .create_response(self.event.id, &self.event.token, &response)
            .await
    }

    pub async fn update(
        &self,
        message: impl Into<String>,
    ) -> Result<twilight_http::Response<Message>, twilight_http::Error> {
        self.interaction()
            .update_response(&self.event.token)
            .content(Some(&message.into()))
            .await
    }

    pub async fn reply(
        &self,
        message: impl Into<String>,
    ) -> Result<twilight_http::Response<EmptyBody>, twilight_http::Error> {
        let response = InteractionResponseDataBuilder::new()
            .content(message)
            .build();

        self.response(InteractionResponse {
            kind: InteractionResponseType::ChannelMessageWithSource,
            data: Some(response),
        })
        .await
    }

    pub async fn defer(&self) -> Result<twilight_http::Response<EmptyBody>, twilight_http::Error> {
        self.response(InteractionResponse {
            kind: InteractionResponseType::DeferredChannelMessageWithSource,
            data: None,
        })
        .await
    }
    pub async fn defer_ephemeral(
        &self,
    ) -> Result<twilight_http::Response<EmptyBody>, twilight_http::Error> {
        self.response(InteractionResponse {
            kind: InteractionResponseType::DeferredChannelMessageWithSource,
            data: Some(
                InteractionResponseDataBuilder::new()
                    .flags(MessageFlags::EPHEMERAL)
                    .build(),
            ),
        })
        .await
    }

    pub fn get_arg_string_optional(&self, name: &str) -> Result<Option<String>, Error> {
        let Some(opt) = self.command.options.iter().find(|opt| opt.name == name) else {
            return Ok(None);
        };

        let CommandOptionValue::String(value) = &opt.value else {
            return Err(format!("option '{}' not a string option", name).into());
        };

        Ok(Some(value.clone()))
    }

    pub fn get_arg_string(&self, name: &str) -> Result<String, Error> {
        self.get_arg_string_optional(name)?
            .ok_or_else(|| format!("couldn't find command argument {}", name).into())
    }
}