Coverage for youversion/core/base_client.py: 100%
319 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-26 11:31 +0100
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-26 11:31 +0100
1"""Base client class implementing common functionality."""
3from datetime import datetime, timezone
4from typing import Any
6from ..enums import MomentKinds
7from ..models.base import Moment
8from ..models.moments import CreateMoment
9from .authenticator import Authenticator
10from .data_processor import DataProcessor
11from .http_client import HttpClient
12from .interfaces import IClient, IHttpClient
15class BaseClient(IClient):
16 """Base client implementing common API operations."""
18 def __init__(self, username: str | None = None, password: str | None = None):
19 """Initialize base client.
21 Args:
22 username: Username for authentication
23 password: Password for authentication
24 """
25 self._authenticator = Authenticator(username, password)
26 self._http_client: IHttpClient | None = None
27 self._data_processor = DataProcessor()
28 self._user_id = None
29 self._access_token = None
31 async def _ensure_authenticated(self) -> None:
32 """Ensure client is authenticated."""
33 if self._http_client is None:
34 client = await self._authenticator.authenticate(
35 self._authenticator.username, self._authenticator.password
36 )
37 self._user_id = self._authenticator.user_id
38 self._access_token = self._authenticator.access_token
39 self._http_client = HttpClient(client, user_id=self._user_id)
41 async def _get_cards_data(
42 self, kind: str = "", page: int = 1
43 ) -> list[dict[str, Any]]:
44 """Get cards data from API.
46 Args:
47 kind: Kind of data to retrieve
48 page: Page number
50 Returns:
51 Raw cards data (list of moments)
52 """
53 await self._ensure_authenticated()
54 data = await self._http_client.get_cards(page=page, kind=kind)
56 # Extract moments array from response structure
57 if "response" not in data:
58 return data
60 moments_data = data.get("response", {}).get("data", {}).get("moments", [])
61 return moments_data
63 async def moments(self, page: int = 1) -> list[Moment]:
64 """Get moments.
66 Args:
67 page: Page number
69 Returns:
70 List of dynamically created Moment objects conforming to
71 MomentProtocol
72 """
73 await self._ensure_authenticated()
74 raw_data = await self._http_client.get_cards(page=page, kind="")
75 # Extract moments array from response structure
76 moments_data = raw_data.get("moments", [])
77 return self._data_processor.process_moments(moments_data)
79 async def highlights(self, page: int = 1) -> list[Any]:
80 """Get highlights.
82 Args:
83 page: Page number
85 Returns:
86 List of Highlight objects
87 """
88 raw_data = await self._get_cards_data(
89 kind=MomentKinds.HIGHLIGHT.value, page=page
90 )
91 raw_data = raw_data.get("moments", [])
92 return self._data_processor.process_highlights(raw_data)
94 async def verse_of_the_day(self, day: int | None = None) -> Any:
95 """Get verse of the day.
96 Note: this does not need authentication
98 Args:
99 day: Specific day number (optional)
101 Returns:
102 Votd object
103 """
104 raw_data = await self._http_client.get_verse_of_the_day()
105 return self._data_processor.process_verse_of_the_day(raw_data, day)
107 async def notes(self, page: int = 1) -> list[Any]:
108 """Get notes.
110 Args:
111 page: Page number
113 Returns:
114 List of note data
115 """
116 raw_data = await self._get_cards_data(kind=MomentKinds.NOTE.value, page=page)
117 raw_data = raw_data.get("moments", [])
118 return self._data_processor.process_notes(raw_data)
120 async def bookmarks(self, page: int = 1) -> list[Any]:
121 """Get bookmarks.
123 Args:
124 page: Page number
126 Returns:
127 List of bookmark data
128 """
129 raw_data = await self._get_cards_data(
130 kind=MomentKinds.BOOKMARK.value, page=page
131 )
132 raw_data = raw_data.get("moments", [])
133 return self._data_processor.process_bookmarks(raw_data)
135 async def my_images(self, page: int = 1) -> list[Any]:
136 """Get images.
138 Args:
139 page: Page number
141 Returns:
142 List of image data
143 """
144 raw_data = await self._get_cards_data(kind=MomentKinds.IMAGE.value, page=page)
145 raw_data = raw_data.get("moments", [])
146 return self._data_processor.process_images(raw_data)
148 async def plan_progress(self, page: int = 1) -> list[Any]:
149 """Get plan progress.
151 Args:
152 page: Page number
154 Returns:
155 List of plan progress data
156 """
157 raw_data = await self._get_cards_data(
158 kind=MomentKinds.PLAN_SEGMENT_COMPLETION.value, page=page
159 )
160 raw_data = raw_data.get("moments", [])
161 return self._data_processor.process_plan_progress(raw_data)
163 async def plan_subscriptions(self, page: int = 1) -> list[Any]:
164 """Get plan subscriptions.
166 Args:
167 page: Page number
169 Returns:
170 List of plan subscription data
171 """
172 raw_data = await self._get_cards_data(
173 kind=MomentKinds.PLAN_SUBSCRIPTION.value, page=page
174 )
175 raw_data = raw_data.get("moments", [])
176 return self._data_processor.process_plan_subscriptions(raw_data)
178 async def plan_completions(self, page: int = 1) -> list[Any]:
179 """Get plan completions.
181 Args:
182 page: Page number
184 Returns:
185 List of plan completion data
186 """
187 raw_data = await self._get_cards_data(
188 kind=MomentKinds.PLAN_COMPLETION.value, page=page
189 )
190 raw_data = raw_data.get("moments", [])
191 return self._data_processor.process_plan_completions(raw_data)
193 async def convert_note_to_md(self) -> list[Any]:
194 """Convert notes to markdown.
196 Returns:
197 List of converted note data
198 """
199 return await self.notes()
201 async def close(self) -> None:
202 """Close the client and cleanup resources."""
203 if self._http_client:
204 await self._http_client.close()
206 @property
207 def username(self) -> str:
208 """Get the username."""
209 return self._authenticator.username
211 @property
212 def user_id(self) -> int | None:
213 """Get the authenticated user ID."""
214 return self._user_id
216 # Bible API methods
217 async def get_bible_configuration(self) -> dict[str, Any]:
218 """Get Bible configuration.
220 Returns:
221 Bible configuration data
222 """
223 await self._ensure_authenticated()
224 raw_data = await self._http_client.get_bible_configuration()
225 return self._data_processor.process_bible_configuration(raw_data)
227 async def get_bible_versions(
228 self, language_tag: str = "eng", version_type: str = "all"
229 ) -> dict[str, Any]:
230 """Get Bible versions for a language.
232 Args:
233 language_tag: Language tag (e.g., 'eng', 'spa')
234 version_type: Type of versions ('all', 'text', 'audio')
236 Returns:
237 Bible versions data
238 """
239 await self._ensure_authenticated()
240 raw_data = await self._http_client.get_bible_versions(
241 language_tag, version_type
242 )
243 return self._data_processor.process_bible_versions(raw_data)
245 async def get_bible_version(self, version_id: int) -> dict[str, Any]:
246 """Get specific Bible version details.
248 Args:
249 version_id: Version ID
251 Returns:
252 Bible version data
253 """
254 await self._ensure_authenticated()
255 raw_data = await self._http_client.get_bible_version(version_id)
256 return self._data_processor.process_bible_version(raw_data)
258 async def get_bible_chapter(
259 self,
260 reference: str,
261 version_id: int = 1,
262 ) -> dict[str, Any]:
263 """Get Bible chapter content.
265 Args:
266 reference: USFM reference (e.g., 'GEN.1')
267 version_id: Version ID
269 Returns:
270 Chapter content data
271 """
272 await self._ensure_authenticated()
273 raw_data = await self._http_client.get_bible_chapter(version_id, reference)
274 return self._data_processor.process_bible_chapter(raw_data)
276 async def get_recommended_languages(self, country: str = "US") -> dict[str, Any]:
277 """Get recommended languages for a country.
279 Args:
280 country: Country code (e.g., 'US', 'CA')
282 Returns:
283 Recommended languages data
284 """
285 await self._ensure_authenticated()
286 raw_data = await self._http_client.get_recommended_languages(country)
287 return self._data_processor.process_recommended_languages(raw_data)
289 # Audio Bible API methods
290 async def get_audio_chapter(
291 self,
292 reference: str,
293 version_id: int = 1,
294 ) -> dict[str, Any]:
295 """Get audio chapter information.
297 Args:
298 reference: USFM reference (e.g., 'GEN.1')
299 version_id: Audio version ID
301 Returns:
302 Audio chapter data
303 """
304 await self._ensure_authenticated()
305 raw_data = await self._http_client.get_audio_chapter(version_id, reference)
306 return self._data_processor.process_audio_chapter(raw_data)
308 async def get_audio_version(self, audio_id: int) -> dict[str, Any]:
309 """Get audio version details.
311 Args:
312 audio_id: Audio version ID
314 Returns:
315 Audio version data
316 """
317 await self._ensure_authenticated()
318 raw_data = await self._http_client.get_audio_version(audio_id)
319 return self._data_processor.process_audio_version(raw_data)
321 # Search API methods
322 async def search_bible(
323 self,
324 query: str,
325 version_id: int | None = None,
326 book: str | None = None,
327 page: int = 1,
328 ) -> dict[str, Any]:
329 """Search Bible text.
331 Args:
332 query: Search query
333 version_id: Version ID (optional)
334 book: Book filter (optional)
335 page: Page number
337 Returns:
338 Search results data
339 """
340 await self._ensure_authenticated()
341 raw_data = await self._http_client.search_bible(query, version_id, book, page)
342 return self._data_processor.process_search_bible(raw_data)
344 async def search_plans(
345 self, query: str, language_tag: str = "en", page: int = 1
346 ) -> dict[str, Any]:
347 """Search reading plans.
349 Args:
350 query: Search query
351 language_tag: Language tag
352 page: Page number
354 Returns:
355 Plan search results data
356 """
357 await self._ensure_authenticated()
358 raw_data = await self._http_client.search_plans(query, language_tag, page)
359 return self._data_processor.process_search_plans(raw_data)
361 async def search_users(
362 self, query: str, language_tag: str = "eng", page: int = 1
363 ) -> dict[str, Any]:
364 """Search users.
366 Args:
367 query: Search query
368 language_tag: Language tag
369 page: Page number
371 Returns:
372 User search results data
373 """
374 await self._ensure_authenticated()
375 raw_data = await self._http_client.search_users(query, language_tag, page)
376 return self._data_processor.process_search_users(raw_data)
378 # Videos API methods
379 async def get_videos(self, language_tag: str = "eng") -> dict[str, Any]:
380 """Get videos list.
382 Args:
383 language_tag: Language tag
385 Returns:
386 Videos data
387 """
388 await self._ensure_authenticated()
389 raw_data = await self._http_client.get_videos(language_tag)
390 return self._data_processor.process_videos(raw_data)
392 async def get_video_details(self, video_id: int) -> dict[str, Any]:
393 """Get video details.
395 Args:
396 video_id: Video ID
398 Returns:
399 Video details data
400 """
401 await self._ensure_authenticated()
402 raw_data = await self._http_client.get_video_details(video_id)
403 return self._data_processor.process_video_details(raw_data)
405 # Badges API methods
406 async def badges(self, page: int = 1) -> list[Any]:
407 """Get badges.
409 Args:
410 page: Page number
412 Returns:
413 List of badge data
414 """
415 raw_data = await self._get_cards_data(kind=MomentKinds.BADGE.value, page=page)
417 if raw_data.get("errors"):
418 raw_data = raw_data.get("errors")
420 return self._data_processor.process_badges(raw_data)
422 # Images API methods
423 async def get_images(
424 self, reference: str, language_tag: str = "eng", page: int = 1
425 ) -> dict[str, Any]:
426 """Get images for a reference.
428 Args:
429 reference: USFM reference
430 language_tag: Language tag
431 page: Page number
433 Returns:
434 Images data
435 """
436 await self._ensure_authenticated()
437 raw_data = await self._http_client.get_images(reference, language_tag, page)
438 raw_data = raw_data.get("images", [])
439 return self._data_processor.process_images(raw_data)
441 async def get_image_upload_url(self) -> dict[str, Any]:
442 """Get image upload URL and parameters.
444 Returns:
445 Upload URL data
446 """
447 await self._ensure_authenticated()
448 raw_data = await self._http_client.get_image_upload_url()
449 return self._data_processor.process_image_upload_url(raw_data)
451 # Events API methods
452 async def search_events(
453 self,
454 query: str,
455 latitude: float | None = None,
456 longitude: float | None = None,
457 page: int = 1,
458 ) -> dict[str, Any]:
459 """Search events.
461 Args:
462 query: Search query
463 latitude: Latitude (optional)
464 longitude: Longitude (optional)
465 page: Page number
467 Returns:
468 Event search results data
469 """
470 await self._ensure_authenticated()
471 raw_data = await self._http_client.search_events(
472 query, latitude, longitude, page
473 )
474 return self._data_processor.process_search_events(raw_data)
476 async def get_event_details(self, event_id: int) -> dict[str, Any]:
477 """Get event details.
479 Args:
480 event_id: Event ID
482 Returns:
483 Event details data
484 """
485 await self._ensure_authenticated()
486 raw_data = await self._http_client.get_event_details(event_id)
487 return self._data_processor.process_event_details(raw_data)
489 async def get_saved_events(self, page: int = 1) -> dict[str, Any]:
490 """Get saved events.
492 Args:
493 page: Page number
495 Returns:
496 Saved events data
497 """
498 await self._ensure_authenticated()
499 raw_data = await self._http_client.get_saved_events(page)
500 return self._data_processor.process_saved_events(raw_data)
502 async def save_event(
503 self, event_id: int, comments: dict[str, Any] | None = None
504 ) -> dict[str, Any]:
505 """Save event.
507 Args:
508 event_id: Event ID
509 comments: Comments (optional)
511 Returns:
512 Save result data
513 """
514 await self._ensure_authenticated()
515 raw_data = await self._http_client.save_event(event_id, comments)
516 return self._data_processor.process_save_event(raw_data)
518 async def delete_saved_event(self, event_id: int) -> dict[str, Any]:
519 """Delete saved event.
521 Args:
522 event_id: Event ID
524 Returns:
525 Delete result data
526 """
527 await self._ensure_authenticated()
528 raw_data = await self._http_client.delete_saved_event(event_id)
529 return self._data_processor.process_delete_saved_event(raw_data)
531 async def get_all_saved_event_ids(self) -> dict[str, Any]:
532 """Get all saved event IDs.
534 Returns:
535 All saved event IDs data
536 """
537 await self._ensure_authenticated()
538 raw_data = await self._http_client.get_all_saved_event_ids()
539 return self._data_processor.process_all_saved_event_ids(raw_data)
541 async def get_event_configuration(self) -> dict[str, Any]:
542 """Get event configuration.
544 Returns:
545 Event configuration data
546 """
547 await self._ensure_authenticated()
548 raw_data = await self._http_client.get_event_configuration()
549 return self._data_processor.process_event_configuration(raw_data)
551 # Moments API methods
552 async def get_moments(
553 self,
554 page: int = 1,
555 user_id: int | None = None,
556 kind: str | None = None,
557 version_id: int | None = None,
558 usfm: str | None = None,
559 ) -> dict[str, Any]:
560 """Get moments list.
562 Args:
563 page: Page number
564 user_id: User ID (optional)
565 kind: Kind of moment (optional)
566 version_id: Bible version ID (optional)
567 usfm: USFM reference (optional)
569 Returns:
570 Moments data
571 """
572 await self._ensure_authenticated()
573 raw_data = await self._http_client.get_moments(
574 page, user_id, kind, version_id, usfm
575 )
576 return self._data_processor.process_moments_list(raw_data)
578 async def get_moment_details(self, moment_id: int) -> dict[str, Any]:
579 """Get moment details.
581 Args:
582 moment_id: Moment ID
584 Returns:
585 Moment details data
586 """
587 await self._ensure_authenticated()
588 raw_data = await self._http_client.get_moment_details(moment_id)
589 return self._data_processor.process_moment_details(raw_data)
591 async def create_moment(
592 self, data: CreateMoment | dict[str, Any]
593 ) -> dict[str, Any]:
594 """Create a new moment.
596 Args:
597 data: Moment data as CreateMoment model or dict
599 Returns:
600 Created moment data
601 """
602 await self._ensure_authenticated()
603 # Convert to CreateMoment if dict is provided
604 if isinstance(data, dict):
605 moment_data = CreateMoment(**data)
606 else:
607 moment_data = data
609 # Add created_dt timestamp
610 moment_dict = moment_data.model_dump()
611 moment_dict["created_dt"] = datetime.now(tz=timezone.utc).isoformat()
613 raw_data = await self._http_client.create_moment(moment_dict)
614 return self._data_processor.process_create_moment(raw_data)
616 async def update_moment(self, data: dict[str, Any]) -> dict[str, Any]:
617 """Update an existing moment.
619 Args:
620 data: Moment data
622 Returns:
623 Updated moment data
624 """
625 await self._ensure_authenticated()
626 raw_data = await self._http_client.update_moment(data)
627 return self._data_processor.process_update_moment(raw_data)
629 async def delete_moment(self, moment_id: int) -> dict[str, Any]:
630 """Delete a moment.
632 Args:
633 moment_id: Moment ID
635 Returns:
636 Delete result data
637 """
638 await self._ensure_authenticated()
639 raw_data = await self._http_client.delete_moment(moment_id)
640 return self._data_processor.process_delete_moment(raw_data)
642 async def get_moment_colors(self) -> dict[str, Any]:
643 """Get available highlight colors.
645 Returns:
646 Colors data
647 """
648 await self._ensure_authenticated()
649 raw_data = await self._http_client.get_moment_colors()
650 return self._data_processor.process_moment_colors(raw_data)
652 async def get_moment_labels(self) -> dict[str, Any]:
653 """Get moment labels.
655 Returns:
656 Labels data
657 """
658 await self._ensure_authenticated()
659 raw_data = await self._http_client.get_moment_labels()
660 return self._data_processor.process_moment_labels(raw_data)
662 async def get_verse_colors(self, usfm: str, version_id: int) -> dict[str, Any]:
663 """Get verse highlight colors.
665 Args:
666 usfm: USFM reference
667 version_id: Bible version ID
669 Returns:
670 Verse colors data
671 """
672 await self._ensure_authenticated()
673 raw_data = await self._http_client.get_verse_colors(usfm, version_id)
674 return self._data_processor.process_verse_colors(raw_data)
676 async def hide_verse_colors(self, data: dict[str, Any]) -> dict[str, Any]:
677 """Hide verse highlight colors.
679 Args:
680 data: Hide colors data
682 Returns:
683 Hide result data
684 """
685 await self._ensure_authenticated()
686 raw_data = await self._http_client.hide_verse_colors(data)
687 return self._data_processor.process_hide_verse_colors(raw_data)
689 async def get_moments_configuration(self) -> dict[str, Any]:
690 """Get moments configuration.
692 Returns:
693 Moments configuration data
694 """
695 await self._ensure_authenticated()
696 raw_data = await self._http_client.get_moments_configuration()
697 return self._data_processor.process_moments_configuration(raw_data)
699 # Comments API methods
700 async def create_comment(self, moment_id: int, comment: str) -> dict[str, Any]:
701 """Create a comment on a moment.
703 Args:
704 moment_id: Moment ID
705 comment: Comment text
707 Returns:
708 Created comment data
709 """
710 await self._ensure_authenticated()
711 raw_data = await self._http_client.create_comment(moment_id, comment)
712 return self._data_processor.process_create_comment(raw_data)
714 async def delete_comment(self, comment_id: int) -> dict[str, Any]:
715 """Delete a comment.
717 Args:
718 comment_id: Comment ID
720 Returns:
721 Delete result data
722 """
723 await self._ensure_authenticated()
724 raw_data = await self._http_client.delete_comment(comment_id)
725 return self._data_processor.process_delete_comment(raw_data)
727 # Likes API methods
728 async def like_moment(self, moment_id: int) -> dict[str, Any]:
729 """Like a moment.
731 Args:
732 moment_id: Moment ID
734 Returns:
735 Like result data
736 """
737 await self._ensure_authenticated()
738 raw_data = await self._http_client.like_moment(moment_id)
739 return self._data_processor.process_like_moment(raw_data)
741 async def unlike_moment(self, moment_id: int) -> dict[str, Any]:
742 """Unlike a moment.
744 Args:
745 moment_id: Moment ID
747 Returns:
748 Unlike result data
749 """
750 await self._ensure_authenticated()
751 raw_data = await self._http_client.unlike_moment(moment_id)
752 return self._data_processor.process_unlike_moment(raw_data)
754 # Messaging API methods
755 async def register_device(
756 self,
757 device_id: str,
758 device_type: str = "android",
759 user_id: int | None = None,
760 old_device_id: str | None = None,
761 tags: str | None = None,
762 ) -> dict[str, Any]:
763 """Register device for push notifications.
765 Args:
766 device_id: Device ID
767 device_type: Device type
768 user_id: User ID (optional)
769 old_device_id: Previous device ID (optional)
770 tags: Device tags (optional)
772 Returns:
773 Registration result data
774 """
775 await self._ensure_authenticated()
776 raw_data = await self._http_client.register_device(
777 device_id, device_type, user_id, old_device_id, tags
778 )
779 return self._data_processor.process_register_device(raw_data)
781 async def unregister_device(self, device_id: str) -> dict[str, Any]:
782 """Unregister device from push notifications.
784 Args:
785 device_id: Device ID
787 Returns:
788 Unregistration result data
789 """
790 await self._ensure_authenticated()
791 raw_data = await self._http_client.unregister_device(device_id)
792 return self._data_processor.process_unregister_device(raw_data)
794 # Themes API methods
795 async def get_themes(
796 self, page: int = 1, language_tag: str = "en"
797 ) -> dict[str, Any]:
798 """Get available themes.
800 Args:
801 page: Page number
802 language_tag: Language tag
804 Returns:
805 Themes data
806 """
807 await self._ensure_authenticated()
808 raw_data = await self._http_client.get_themes(page, language_tag)
809 return self._data_processor.process_themes(raw_data)
811 async def add_theme(
812 self,
813 theme_id: int,
814 available_locales: list[str],
815 colors: dict[str, Any],
816 cta_urls: dict[str, Any],
817 msgid_suffix: str,
818 version_ids: dict[str, int],
819 ) -> dict[str, Any]:
820 """Add a theme to user's collection.
822 Args:
823 theme_id: Theme ID
824 available_locales: List of available locale codes
825 colors: Theme colors dictionary
826 cta_urls: Call-to-action URLs dictionary
827 msgid_suffix: Message ID suffix
828 version_ids: Dictionary of version IDs by locale code
830 Returns:
831 Add result data
832 """
833 await self._ensure_authenticated()
834 raw_data = await self._http_client.add_theme(
835 theme_id, available_locales, colors, cta_urls, msgid_suffix, version_ids
836 )
837 return self._data_processor.process_add_theme(raw_data)
839 async def remove_theme(self, theme_id: int) -> dict[str, Any]:
840 """Remove a theme from user's collection.
842 Args:
843 theme_id: Theme ID
845 Returns:
846 Remove result data
847 """
848 await self._ensure_authenticated()
849 raw_data = await self._http_client.remove_theme(theme_id)
850 return self._data_processor.process_remove_theme(raw_data)
852 async def set_theme(
853 self, theme_id: int, previous_theme_id: int | None = None
854 ) -> dict[str, Any]:
855 """Set active theme.
857 Args:
858 theme_id: Theme ID
859 previous_theme_id: Previous theme ID (optional)
861 Returns:
862 Set result data
863 """
864 await self._ensure_authenticated()
865 raw_data = await self._http_client.set_theme(theme_id, previous_theme_id)
866 return self._data_processor.process_set_theme(raw_data)
868 async def get_theme_description(
869 self, theme_id: int, language_tag: str = "eng"
870 ) -> dict[str, Any]:
871 """Get theme description.
873 Args:
874 theme_id: Theme ID
875 language_tag: Language tag
877 Returns:
878 Theme description data
879 """
880 await self._ensure_authenticated()
881 raw_data = await self._http_client.get_theme_description(theme_id, language_tag)
882 return self._data_processor.process_theme_description(raw_data)
884 # Friendships API methods
885 async def send_friend_request(self, user_id: int) -> dict[str, Any]:
886 """Send a friend request to a user.
888 Args:
889 user_id: User ID to send friend request to
891 Returns:
892 Friend request response data with incoming and outgoing lists
893 """
894 await self._ensure_authenticated()
895 raw_data = await self._http_client.send_friend_request(user_id)
896 return self._data_processor.process_send_friend_request(raw_data)
898 # Friends API methods
899 async def get_friends(self, page: int = 1) -> dict[str, Any]:
900 """Get the authenticated user's friends list.
902 Args:
903 page: Page number for pagination.
905 Returns:
906 Friends list data.
907 """
908 await self._ensure_authenticated()
909 return await self._http_client.get_friends(page)
911 async def get_all_friends(self, page: int = 1) -> dict[str, Any]:
912 """Get all friends including extended metadata.
914 Args:
915 page: Page number for pagination.
917 Returns:
918 All friends data.
919 """
920 await self._ensure_authenticated()
921 return await self._http_client.get_all_friends(page)
923 async def delete_friend(self, user_id: int) -> dict[str, Any]:
924 """Remove a friend by user ID.
926 Args:
927 user_id: Friend's YouVersion user ID.
929 Returns:
930 Delete operation result data.
931 """
932 await self._ensure_authenticated()
933 return await self._http_client.delete_friend(user_id)
935 # Extended friendships API methods
936 async def get_incoming_friend_requests(self, page: int = 1) -> dict[str, Any]:
937 """Get incoming friend requests.
939 Args:
940 page: Page number for pagination.
942 Returns:
943 Incoming friend request data.
944 """
945 await self._ensure_authenticated()
946 return await self._http_client.get_incoming_friend_requests(page)
948 async def get_friend_suggestions(
949 self, page: int = 1, language_tag: str = "en"
950 ) -> dict[str, Any]:
951 """Get friend suggestions.
953 Args:
954 page: Page number for pagination.
955 language_tag: ISO 639-1 locale (e.g. ``en``).
957 Returns:
958 Friend suggestion data.
959 """
960 await self._ensure_authenticated()
961 return await self._http_client.get_friend_suggestions(page, language_tag)
963 async def accept_friend_request(self, user_id: int) -> dict[str, Any]:
964 """Accept an incoming friend request.
966 Args:
967 user_id: Requester's user ID.
969 Returns:
970 Accept operation result data.
971 """
972 await self._ensure_authenticated()
973 return await self._http_client.accept_friend_request(user_id)
975 async def decline_friend_request(self, user_id: int) -> dict[str, Any]:
976 """Decline an incoming friend request.
978 Args:
979 user_id: Requester's user ID.
981 Returns:
982 Decline operation result data.
983 """
984 await self._ensure_authenticated()
985 return await self._http_client.decline_friend_request(user_id)
987 async def dismiss_friend_suggestion(self, user_id: int) -> dict[str, Any]:
988 """Dismiss a friend suggestion.
990 Args:
991 user_id: Suggested user's ID.
993 Returns:
994 Dismiss operation result data.
995 """
996 await self._ensure_authenticated()
997 return await self._http_client.dismiss_friend_suggestion(user_id)
999 async def get_facebook_friends(self, page: int = 1) -> dict[str, Any]:
1000 """Get Facebook-linked friend suggestions.
1002 Args:
1003 page: Page number for pagination.
1005 Returns:
1006 Facebook friends data.
1007 """
1008 await self._ensure_authenticated()
1009 return await self._http_client.get_facebook_friends(page)
1011 async def sync_friendship_contacts(
1012 self, contacts: list[dict[str, Any]]
1013 ) -> dict[str, Any]:
1014 """Upload contacts for friend matching.
1016 Args:
1017 contacts: Contact records to sync.
1019 Returns:
1020 Contact sync result data.
1021 """
1022 await self._ensure_authenticated()
1023 return await self._http_client.sync_friendship_contacts(contacts)
1025 # Notifications API methods
1026 async def get_notifications(self, page: int = 1) -> dict[str, Any]:
1027 """Get notification feed items.
1029 Args:
1030 page: Page number for pagination.
1032 Returns:
1033 Notification feed data.
1034 """
1035 await self._ensure_authenticated()
1036 return await self._http_client.get_notifications(page)
1038 async def get_notification_settings(self) -> dict[str, Any]:
1039 """Get notification settings for the authenticated user.
1041 Returns:
1042 Notification settings data.
1043 """
1044 await self._ensure_authenticated()
1045 return await self._http_client.get_notification_settings()
1047 async def update_notification_settings(
1048 self, data: dict[str, Any]
1049 ) -> dict[str, Any]:
1050 """Update notification settings.
1052 Args:
1053 data: Settings fields to update.
1055 Returns:
1056 Updated settings data.
1057 """
1058 await self._ensure_authenticated()
1059 return await self._http_client.update_notification_settings(data)
1061 async def update_notifications(self, data: dict[str, Any]) -> dict[str, Any]:
1062 """Update notification read state.
1064 Args:
1065 data: Notification update payload.
1067 Returns:
1068 Update result data.
1069 """
1070 await self._ensure_authenticated()
1071 return await self._http_client.update_notifications(data)
1073 async def get_votd_notification_settings(self) -> dict[str, Any]:
1074 """Get verse-of-the-day notification settings.
1076 Returns:
1077 VOTD notification settings.
1078 """
1079 await self._ensure_authenticated()
1080 return await self._http_client.get_votd_notification_settings()
1082 async def update_votd_notification_settings(
1083 self, data: dict[str, Any]
1084 ) -> dict[str, Any]:
1085 """Update verse-of-the-day notification settings.
1087 Args:
1088 data: VOTD settings to update.
1090 Returns:
1091 Updated VOTD settings data.
1092 """
1093 await self._ensure_authenticated()
1094 return await self._http_client.update_votd_notification_settings(data)
1096 # Share API methods
1097 async def invite_by_email(self, data: dict[str, Any]) -> dict[str, Any]:
1098 """Send a YouVersion invite by email.
1100 Args:
1101 data: Invite payload.
1103 Returns:
1104 Invite operation result data.
1105 """
1106 await self._ensure_authenticated()
1107 return await self._http_client.invite_by_email(data)
1109 async def invite_by_sms(self, data: dict[str, Any]) -> dict[str, Any]:
1110 """Send a YouVersion invite by SMS.
1112 Args:
1113 data: Invite payload.
1115 Returns:
1116 Invite operation result data.
1117 """
1118 await self._ensure_authenticated()
1119 return await self._http_client.invite_by_sms(data)
1121 # Additional moments and search API methods
1122 async def get_client_side_moments(self, page: int = 1) -> dict[str, Any]:
1123 """Get client-side moment items.
1125 Args:
1126 page: Page number for pagination.
1128 Returns:
1129 Client-side moments data.
1130 """
1131 await self._ensure_authenticated()
1132 return await self._http_client.get_client_side_moments(page)
1134 async def get_moments_votd(self, language_tag: str | None = None) -> dict[str, Any]:
1135 """Get verse of the day from the moments API.
1137 Args:
1138 language_tag: Optional language tag; omit for default VOTD payload.
1140 Returns:
1141 VOTD data from moments service.
1142 """
1143 await self._ensure_authenticated()
1144 return await self._http_client.get_moments_votd(language_tag)