Skip to main content

nylas_types/
notetaker.rs

1//! Notetaker types for meeting recording and transcription.
2
3use crate::common::{NotetakerId, RecordingId};
4use serde::{Deserialize, Serialize};
5
6/// Notetaker configuration.
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct Notetaker {
9    /// Unique identifier for the notetaker configuration
10    pub id: NotetakerId,
11
12    /// Grant ID this notetaker belongs to
13    pub grant_id: String,
14
15    /// Whether the notetaker is active
16    pub active: bool,
17
18    /// Provider-specific settings
19    pub provider_settings: ProviderSettings,
20
21    /// Transcription settings
22    pub transcription_settings: Option<TranscriptionSettings>,
23
24    /// AI summary settings
25    pub summary_settings: Option<SummarySettings>,
26
27    /// Webhook URL for notifications
28    pub webhook_url: Option<String>,
29
30    /// Created timestamp (Unix seconds)
31    pub created_at: i64,
32
33    /// Updated timestamp (Unix seconds)
34    pub updated_at: i64,
35}
36
37/// Provider-specific settings.
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(tag = "provider", rename_all = "lowercase")]
40pub enum ProviderSettings {
41    /// Google Meet settings
42    #[serde(rename = "google-meet")]
43    GoogleMeet {
44        /// Auto-join meetings on calendar
45        auto_join: bool,
46        /// Recording quality
47        quality: RecordingQuality,
48    },
49
50    /// Microsoft Teams settings
51    #[serde(rename = "microsoft-teams")]
52    MicrosoftTeams {
53        /// Auto-join meetings on calendar
54        auto_join: bool,
55        /// Recording quality
56        quality: RecordingQuality,
57    },
58
59    /// Zoom settings
60    Zoom {
61        /// Auto-join meetings on calendar
62        auto_join: bool,
63        /// Recording quality
64        quality: RecordingQuality,
65        /// Cloud recording (vs local)
66        cloud_recording: bool,
67    },
68}
69
70/// Recording quality levels.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(rename_all = "lowercase")]
73pub enum RecordingQuality {
74    /// Standard definition
75    Standard,
76    /// High definition
77    Hd,
78}
79
80/// Transcription settings.
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct TranscriptionSettings {
83    /// Enable transcription
84    pub enabled: bool,
85
86    /// Language for transcription (e.g., "en-US")
87    pub language: Option<String>,
88
89    /// Enable speaker identification
90    pub speaker_identification: bool,
91
92    /// Profanity filtering
93    pub profanity_filter: Option<bool>,
94}
95
96/// AI summary settings.
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98pub struct SummarySettings {
99    /// Enable AI summaries
100    pub enabled: bool,
101
102    /// Extract action items
103    pub extract_action_items: bool,
104
105    /// Extract key topics
106    pub extract_topics: bool,
107
108    /// Extract decisions made
109    pub extract_decisions: bool,
110
111    /// Summary length preference
112    pub summary_length: SummaryLength,
113}
114
115/// Summary length preference.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(rename_all = "lowercase")]
118pub enum SummaryLength {
119    /// Brief summary
120    Brief,
121    /// Medium-length summary
122    Medium,
123    /// Detailed summary
124    Detailed,
125}
126
127/// Recording from a meeting.
128#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
129pub struct Recording {
130    /// Unique identifier for the recording
131    pub id: RecordingId,
132
133    /// Notetaker configuration ID
134    pub notetaker_id: NotetakerId,
135
136    /// Meeting title
137    pub title: String,
138
139    /// Meeting start time (Unix seconds)
140    pub start_time: i64,
141
142    /// Meeting end time (Unix seconds)
143    pub end_time: Option<i64>,
144
145    /// Recording duration in seconds
146    pub duration: Option<i64>,
147
148    /// Recording status
149    pub status: RecordingStatus,
150
151    /// Recording file URL (when available)
152    pub recording_url: Option<String>,
153
154    /// Transcription (when available)
155    pub transcription: Option<Transcription>,
156
157    /// AI-generated summary (when available)
158    pub summary: Option<Summary>,
159
160    /// Meeting participants
161    pub participants: Vec<MeetingParticipant>,
162
163    /// Created timestamp (Unix seconds)
164    pub created_at: i64,
165
166    /// Updated timestamp (Unix seconds)
167    pub updated_at: i64,
168}
169
170/// Recording status.
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
172#[serde(rename_all = "lowercase")]
173pub enum RecordingStatus {
174    /// Recording in progress
175    Recording,
176    /// Processing (transcribing, summarizing)
177    Processing,
178    /// Completed and available
179    Completed,
180    /// Failed
181    Failed,
182}
183
184/// Meeting transcription.
185#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
186pub struct Transcription {
187    /// Full transcript text
188    pub text: String,
189
190    /// Transcript segments with timestamps
191    pub segments: Vec<TranscriptSegment>,
192
193    /// Detected language
194    pub language: String,
195
196    /// Confidence score (0.0 - 1.0)
197    pub confidence: f64,
198}
199
200/// Transcript segment with speaker and timing.
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202pub struct TranscriptSegment {
203    /// Segment text
204    pub text: String,
205
206    /// Speaker identifier
207    pub speaker: Option<String>,
208
209    /// Start time in seconds from recording start
210    pub start_time: f64,
211
212    /// End time in seconds from recording start
213    pub end_time: f64,
214
215    /// Confidence score (0.0 - 1.0)
216    pub confidence: f64,
217}
218
219/// AI-generated meeting summary.
220#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
221pub struct Summary {
222    /// Overall meeting summary
223    pub summary: String,
224
225    /// Extracted action items
226    pub action_items: Vec<ActionItem>,
227
228    /// Key topics discussed
229    pub topics: Vec<String>,
230
231    /// Decisions made
232    pub decisions: Vec<String>,
233
234    /// Next steps
235    pub next_steps: Vec<String>,
236}
237
238/// Action item from meeting.
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub struct ActionItem {
241    /// Description of the action
242    pub description: String,
243
244    /// Assigned to (if detected)
245    pub assignee: Option<String>,
246
247    /// Due date (if mentioned, Unix seconds)
248    pub due_date: Option<i64>,
249
250    /// Priority level (if detected)
251    pub priority: Option<Priority>,
252}
253
254/// Action item priority.
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
256#[serde(rename_all = "lowercase")]
257pub enum Priority {
258    /// High priority
259    High,
260    /// Medium priority
261    Medium,
262    /// Low priority
263    Low,
264}
265
266/// Meeting participant.
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
268pub struct MeetingParticipant {
269    /// Participant name
270    pub name: String,
271
272    /// Email address (if available)
273    pub email: Option<String>,
274
275    /// Join time (Unix seconds)
276    pub joined_at: i64,
277
278    /// Leave time (Unix seconds, None if still in meeting)
279    pub left_at: Option<i64>,
280
281    /// Speaking time in seconds
282    pub speaking_time: Option<i64>,
283}
284
285/// Request to create a notetaker configuration.
286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
287pub struct CreateNotetakerRequest {
288    /// Provider-specific settings
289    pub provider_settings: ProviderSettings,
290
291    /// Transcription settings
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub transcription_settings: Option<TranscriptionSettings>,
294
295    /// AI summary settings
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub summary_settings: Option<SummarySettings>,
298
299    /// Webhook URL for notifications
300    #[serde(skip_serializing_if = "Option::is_none")]
301    pub webhook_url: Option<String>,
302
303    /// Whether the notetaker is active
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub active: Option<bool>,
306}
307
308impl CreateNotetakerRequest {
309    /// Create a builder for CreateNotetakerRequest.
310    pub fn builder(provider_settings: ProviderSettings) -> CreateNotetakerRequestBuilder {
311        CreateNotetakerRequestBuilder::new(provider_settings)
312    }
313}
314
315/// Builder for CreateNotetakerRequest.
316#[derive(Debug)]
317pub struct CreateNotetakerRequestBuilder {
318    request: CreateNotetakerRequest,
319}
320
321impl CreateNotetakerRequestBuilder {
322    /// Create a new builder.
323    pub fn new(provider_settings: ProviderSettings) -> Self {
324        Self {
325            request: CreateNotetakerRequest {
326                provider_settings,
327                transcription_settings: None,
328                summary_settings: None,
329                webhook_url: None,
330                active: None,
331            },
332        }
333    }
334
335    /// Set transcription settings.
336    pub fn transcription_settings(mut self, settings: TranscriptionSettings) -> Self {
337        self.request.transcription_settings = Some(settings);
338        self
339    }
340
341    /// Set summary settings.
342    pub fn summary_settings(mut self, settings: SummarySettings) -> Self {
343        self.request.summary_settings = Some(settings);
344        self
345    }
346
347    /// Set webhook URL.
348    pub fn webhook_url(mut self, url: impl Into<String>) -> Self {
349        self.request.webhook_url = Some(url.into());
350        self
351    }
352
353    /// Set active status.
354    pub fn active(mut self, active: bool) -> Self {
355        self.request.active = Some(active);
356        self
357    }
358
359    /// Build the request.
360    pub fn build(self) -> CreateNotetakerRequest {
361        self.request
362    }
363}
364
365/// Request to update a notetaker configuration.
366#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
367pub struct UpdateNotetakerRequest {
368    /// Provider-specific settings
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub provider_settings: Option<ProviderSettings>,
371
372    /// Transcription settings
373    #[serde(skip_serializing_if = "Option::is_none")]
374    pub transcription_settings: Option<TranscriptionSettings>,
375
376    /// AI summary settings
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub summary_settings: Option<SummarySettings>,
379
380    /// Webhook URL for notifications
381    #[serde(skip_serializing_if = "Option::is_none")]
382    pub webhook_url: Option<String>,
383
384    /// Whether the notetaker is active
385    #[serde(skip_serializing_if = "Option::is_none")]
386    pub active: Option<bool>,
387}
388
389impl UpdateNotetakerRequest {
390    /// Create a builder for UpdateNotetakerRequest.
391    pub fn builder() -> UpdateNotetakerRequestBuilder {
392        UpdateNotetakerRequestBuilder::default()
393    }
394}
395
396/// Builder for UpdateNotetakerRequest.
397#[derive(Debug, Default)]
398pub struct UpdateNotetakerRequestBuilder {
399    request: UpdateNotetakerRequest,
400}
401
402impl UpdateNotetakerRequestBuilder {
403    /// Set provider settings.
404    pub fn provider_settings(mut self, settings: ProviderSettings) -> Self {
405        self.request.provider_settings = Some(settings);
406        self
407    }
408
409    /// Set transcription settings.
410    pub fn transcription_settings(mut self, settings: TranscriptionSettings) -> Self {
411        self.request.transcription_settings = Some(settings);
412        self
413    }
414
415    /// Set summary settings.
416    pub fn summary_settings(mut self, settings: SummarySettings) -> Self {
417        self.request.summary_settings = Some(settings);
418        self
419    }
420
421    /// Set webhook URL.
422    pub fn webhook_url(mut self, url: impl Into<String>) -> Self {
423        self.request.webhook_url = Some(url.into());
424        self
425    }
426
427    /// Set active status.
428    pub fn active(mut self, active: bool) -> Self {
429        self.request.active = Some(active);
430        self
431    }
432
433    /// Build the request.
434    pub fn build(self) -> UpdateNotetakerRequest {
435        self.request
436    }
437}
438
439#[cfg(test)]
440mod tests {
441    use super::*;
442
443    #[test]
444    fn test_notetaker_id_creation() {
445        let id = NotetakerId::new("notetaker_123");
446        assert_eq!(id.as_str(), "notetaker_123");
447    }
448
449    #[test]
450    fn test_recording_id_creation() {
451        let id = RecordingId::new("rec_456");
452        assert_eq!(id.as_str(), "rec_456");
453    }
454
455    #[test]
456    fn test_create_notetaker_request_builder() {
457        let provider = ProviderSettings::GoogleMeet {
458            auto_join: true,
459            quality: RecordingQuality::Hd,
460        };
461
462        let transcription = TranscriptionSettings {
463            enabled: true,
464            language: Some("en-US".to_string()),
465            speaker_identification: true,
466            profanity_filter: Some(false),
467        };
468
469        let request = CreateNotetakerRequest::builder(provider)
470            .transcription_settings(transcription)
471            .active(true)
472            .build();
473
474        assert!(request.active.unwrap());
475        assert!(request.transcription_settings.is_some());
476    }
477
478    #[test]
479    fn test_update_notetaker_request_builder() {
480        let request = UpdateNotetakerRequest::builder()
481            .active(false)
482            .webhook_url("https://example.com/webhook")
483            .build();
484
485        assert_eq!(request.active, Some(false));
486        assert_eq!(
487            request.webhook_url,
488            Some("https://example.com/webhook".to_string())
489        );
490    }
491
492    #[test]
493    fn test_recording_status_serialization() {
494        let status = RecordingStatus::Processing;
495        let json = serde_json::to_string(&status).unwrap();
496        assert_eq!(json, "\"processing\"");
497    }
498
499    #[test]
500    fn test_summary_length_serialization() {
501        let length = SummaryLength::Brief;
502        let json = serde_json::to_string(&length).unwrap();
503        assert_eq!(json, "\"brief\"");
504    }
505}