1use std::any::Any;
2use std::sync::Arc;
3
4use crate::action::{ChatAction, ChatActionGuard, ChatActionSender};
5
6#[cfg(not(target_arch = "wasm32"))]
7pub trait ContextDataBounds: Send + Sync {}
8#[cfg(not(target_arch = "wasm32"))]
9impl<T: Send + Sync + ?Sized> ContextDataBounds for T {}
10
11#[cfg(target_arch = "wasm32")]
12pub trait ContextDataBounds {}
13#[cfg(target_arch = "wasm32")]
14impl<T: ?Sized> ContextDataBounds for T {}
15
16#[derive(Clone)]
21pub struct Context {
22 inner: Arc<dyn ContextData>,
23}
24
25impl Context {
26 pub fn new<T: ContextData>(data: T) -> Self {
28 Self {
29 inner: Arc::new(data),
30 }
31 }
32
33 pub fn channel_id(&self) -> &str {
35 self.inner.channel_id()
36 }
37
38 pub fn user_id(&self) -> &str {
40 self.inner.user_id()
41 }
42
43 pub fn user_name(&self) -> &str {
45 self.inner.user_name()
46 }
47
48 pub fn command_name(&self) -> Option<&str> {
50 self.inner.command_name()
51 }
52
53 pub fn command_args(&self) -> Option<&str> {
55 self.inner.command_args()
56 }
57
58 pub fn option(&self, name: &str) -> Option<OptionValue> {
60 self.inner.option(name)
61 }
62
63 pub fn button_id(&self) -> Option<&str> {
65 self.inner.button_id()
66 }
67
68 pub fn message_content(&self) -> Option<&str> {
70 self.inner.message_content()
71 }
72
73 pub fn platform<T: 'static>(&self) -> Option<&T> {
77 self.inner.as_any().downcast_ref::<T>()
78 }
79
80 pub fn data(&self) -> &dyn ContextData {
82 &*self.inner
83 }
84
85 pub fn typing(&self) -> Option<ChatActionGuard> {
100 self.send_action(ChatAction::Typing)
101 }
102
103 pub(crate) fn send_action(&self, action: ChatAction) -> Option<ChatActionGuard> {
107 let sender = self.inner.action_sender()?;
108 Some(ChatActionGuard::start(sender, action))
109 }
110}
111
112#[derive(Debug, Clone)]
114pub enum OptionValue {
115 String(String),
116 Integer(i64),
117 Boolean(bool),
118 Number(f64),
119}
120
121impl OptionValue {
122 pub fn as_str(&self) -> Option<&str> {
123 match self {
124 Self::String(s) => Some(s),
125 _ => None,
126 }
127 }
128
129 pub fn as_i64(&self) -> Option<i64> {
130 match self {
131 Self::Integer(i) => Some(*i),
132 _ => None,
133 }
134 }
135
136 pub fn as_bool(&self) -> Option<bool> {
137 match self {
138 Self::Boolean(b) => Some(*b),
139 _ => None,
140 }
141 }
142
143 pub fn as_f64(&self) -> Option<f64> {
144 match self {
145 Self::Number(n) => Some(*n),
146 _ => None,
147 }
148 }
149}
150
151pub trait ContextData: ContextDataBounds + 'static {
156 fn channel_id(&self) -> &str;
157 fn user_id(&self) -> &str;
158 fn user_name(&self) -> &str;
159 fn command_name(&self) -> Option<&str>;
160 fn command_args(&self) -> Option<&str>;
161 fn option(&self, name: &str) -> Option<OptionValue>;
162 fn button_id(&self) -> Option<&str>;
163 fn message_content(&self) -> Option<&str>;
164 fn as_any(&self) -> &dyn Any;
165
166 fn action_sender(&self) -> Option<Box<dyn ChatActionSender>> {
171 None
172 }
173}