|
47 | 47 | from .bot import Bot
|
48 | 48 | from .components import Component
|
49 | 49 | from .core import Command
|
| 50 | + from .translators import Translator |
50 | 51 |
|
51 | 52 | PrefixT: TypeAlias = str | Iterable[str] | Callable[[Bot, ChatMessage], Coroutine[Any, Any, str | Iterable[str]]]
|
52 | 53 |
|
@@ -255,6 +256,15 @@ def type(self) -> ContextType:
|
255 | 256 | """
|
256 | 257 | return self._type
|
257 | 258 |
|
| 259 | + @property |
| 260 | + def translator(self) -> Translator[Any] | None: |
| 261 | + """Property returning the :class:`.commands.Translator` assigned to the :class:`.commands.Command` if found or |
| 262 | + ``None``. This property will always return ``None`` if no valid command or prefix is associated with this Context.""" |
| 263 | + if not self.command: |
| 264 | + return None |
| 265 | + |
| 266 | + return self.command.translator |
| 267 | + |
258 | 268 | @property
|
259 | 269 | def error_dispatched(self) -> bool:
|
260 | 270 | return self._error_dispatched
|
@@ -543,6 +553,80 @@ async def send(self, content: str, *, me: bool = False) -> SentMessage:
|
543 | 553 | new = (f"/me {content}" if me else content).strip()
|
544 | 554 | return await self.channel.send_message(sender=self.bot.bot_id, message=new)
|
545 | 555 |
|
| 556 | + async def send_translated(self, content: str, *, me: bool = False, langcode: str | None = None) -> SentMessage: |
| 557 | + """|coro| |
| 558 | +
|
| 559 | + Send a translated chat message to the channel associated with this context. |
| 560 | +
|
| 561 | + You must have added a :class:`.commands.Translator` to your :class:`.commands.Command` in order to effectively use |
| 562 | + this method. If no :class:`.commands.Translator` is found, this method acts identical to :meth:`.send`. |
| 563 | +
|
| 564 | + If this method can not find a valid language code, E.g. both :meth:`.commands.Translator.get_langcode` and the parameter |
| 565 | + ``langcode`` return ``None``, this method acts identical to :meth:`.send`. |
| 566 | +
|
| 567 | + See the following documentation for more details on translators: |
| 568 | +
|
| 569 | + - :class:`.commands.Translator` |
| 570 | + - :func:`.commands.translator` |
| 571 | +
|
| 572 | + .. important:: |
| 573 | +
|
| 574 | + You must have the ``user:write:chat`` scope. If an app access token is used, |
| 575 | + then additionally requires the ``user:bot`` scope on the bot, |
| 576 | + and either ``channel:bot`` scope from the broadcaster or moderator status. |
| 577 | +
|
| 578 | + Parameters |
| 579 | + ---------- |
| 580 | + content: str |
| 581 | + The content of the message you would like to translate and then send. |
| 582 | + This **and** the translated version of this content cannot exceed ``500`` characters. |
| 583 | + Additionally the content parameter will be stripped of all leading and trailing whitespace. |
| 584 | + me: bool |
| 585 | + An optional bool indicating whether you would like to send this message with the ``/me`` chat command. |
| 586 | + langcode: str | None |
| 587 | + An optional ``langcode`` to override the ``langcode`` returned from :meth:`.commands.Translator.get_langcode`. |
| 588 | + This should only be provided if you do custom language code lookups outside of your |
| 589 | + :class:`.commands.Translator`. Defaults to ``None``. |
| 590 | +
|
| 591 | +
|
| 592 | + Returns |
| 593 | + ------- |
| 594 | + SentMessage |
| 595 | + The payload received by Twitch after sending this message. |
| 596 | +
|
| 597 | + Raises |
| 598 | + ------ |
| 599 | + HTTPException |
| 600 | + Twitch failed to process the message, could be ``400``, ``401``, ``403``, ``422`` or any ``5xx`` status code. |
| 601 | + MessageRejectedError |
| 602 | + Twitch rejected the message from various checks. |
| 603 | + TranslatorError |
| 604 | + An error occurred during translation. |
| 605 | + """ |
| 606 | + translator: Translator[Any] | None = self.translator |
| 607 | + new = (f"/me {content}" if me else content).strip() |
| 608 | + |
| 609 | + if not self.command or not translator: |
| 610 | + return await self.channel.send_message(sender=self.bot.bot_id, message=new) |
| 611 | + |
| 612 | + invoked = self.invoked_with |
| 613 | + |
| 614 | + try: |
| 615 | + code = langcode or translator.get_langcode(self, invoked.lower()) if invoked else None |
| 616 | + except Exception as e: |
| 617 | + raise TranslatorError(f"An exception occurred fetching a language code for '{invoked}'.", original=e) from e |
| 618 | + |
| 619 | + if code is None: |
| 620 | + return await self.channel.send_message(sender=self.bot.bot_id, message=new) |
| 621 | + |
| 622 | + try: |
| 623 | + translated = await translator.translate(self, content, code) |
| 624 | + except Exception as e: |
| 625 | + raise TranslatorError(f"An exception occurred translating content for '{invoked}'.", original=e) from e |
| 626 | + |
| 627 | + new_translated = (f"/me {translated}" if me else translated).strip() |
| 628 | + return await self.channel.send_message(sender=self.bot.bot_id, message=new_translated) |
| 629 | + |
546 | 630 | async def reply(self, content: str, *, me: bool = False) -> SentMessage:
|
547 | 631 | """|coro|
|
548 | 632 |
|
@@ -588,6 +672,93 @@ async def reply(self, content: str, *, me: bool = False) -> SentMessage:
|
588 | 672 | new = (f"/me {content}" if me else content).strip()
|
589 | 673 | return await self.channel.send_message(sender=self.bot.bot_id, message=new, reply_to_message_id=self._payload.id)
|
590 | 674 |
|
| 675 | + async def reply_translated(self, content: str, *, me: bool = False, langcode: str | None = None) -> SentMessage: |
| 676 | + """|coro| |
| 677 | +
|
| 678 | + Send a translated chat message as a reply to the user who this message is associated with and to the channel associated with |
| 679 | + this context. |
| 680 | +
|
| 681 | + You must have added a :class:`.commands.Translator` to your :class:`.commands.Command` in order to effectively use |
| 682 | + this method. If no :class:`.commands.Translator` is found, this method acts identical to :meth:`.reply`. |
| 683 | +
|
| 684 | + If this method can not find a valid language code, E.g. both :meth:`.commands.Translator.get_langcode` and the parameter |
| 685 | + ``langcode`` return ``None``, this method acts identical to :meth:`.reply`. |
| 686 | +
|
| 687 | + See the following documentation for more details on translators: |
| 688 | +
|
| 689 | + - :class:`.commands.Translator` |
| 690 | + - :func:`.commands.translator` |
| 691 | +
|
| 692 | + .. warning:: |
| 693 | +
|
| 694 | + You cannot use this method in Reward based context. E.g. |
| 695 | + if :attr:`~.commands.Context.type` is :attr:`~.commands.ContextType.REWARD`. |
| 696 | +
|
| 697 | + .. important:: |
| 698 | +
|
| 699 | + You must have the ``user:write:chat`` scope. If an app access token is used, |
| 700 | + then additionally requires the ``user:bot`` scope on the bot, |
| 701 | + and either ``channel:bot`` scope from the broadcaster or moderator status. |
| 702 | +
|
| 703 | + Parameters |
| 704 | + ---------- |
| 705 | + content: str |
| 706 | + The content of the message you would like to translate and then send. |
| 707 | + This **and** the translated version of this content cannot exceed ``500`` characters. |
| 708 | + Additionally the content parameter will be stripped of all leading and trailing whitespace. |
| 709 | + me: bool |
| 710 | + An optional bool indicating whether you would like to send this message with the ``/me`` chat command. |
| 711 | + langcode: str | None |
| 712 | + An optional ``langcode`` to override the ``langcode`` returned from :meth:`.commands.Translator.get_langcode`. |
| 713 | + This should only be provided if you do custom language code lookups outside of your |
| 714 | + :class:`.commands.Translator`. Defaults to ``None``. |
| 715 | +
|
| 716 | +
|
| 717 | + Returns |
| 718 | + ------- |
| 719 | + SentMessage |
| 720 | + The payload received by Twitch after sending this message. |
| 721 | +
|
| 722 | + Raises |
| 723 | + ------ |
| 724 | + HTTPException |
| 725 | + Twitch failed to process the message, could be ``400``, ``401``, ``403``, ``422`` or any ``5xx`` status code. |
| 726 | + MessageRejectedError |
| 727 | + Twitch rejected the message from various checks. |
| 728 | + TranslatorError |
| 729 | + An error occurred during translation. |
| 730 | + """ |
| 731 | + if self._type is ContextType.REWARD: |
| 732 | + raise TypeError("Cannot reply to a message in a Reward based context.") |
| 733 | + |
| 734 | + translator: Translator[Any] | None = self.translator |
| 735 | + new = (f"/me {content}" if me else content).strip() |
| 736 | + |
| 737 | + if not self.command or not translator: |
| 738 | + return await self.channel.send_message(sender=self.bot.bot_id, message=new, reply_to_message_id=self._payload.id) |
| 739 | + |
| 740 | + invoked = self.invoked_with |
| 741 | + |
| 742 | + try: |
| 743 | + code = langcode or translator.get_langcode(self, invoked.lower()) if invoked else None |
| 744 | + except Exception as e: |
| 745 | + raise TranslatorError(f"An exception occurred fetching a language code for '{invoked}'.", original=e) from e |
| 746 | + |
| 747 | + if code is None: |
| 748 | + return await self.channel.send_message(sender=self.bot.bot_id, message=new, reply_to_message_id=self._payload.id) |
| 749 | + |
| 750 | + try: |
| 751 | + translated = await translator.translate(self, content, code) |
| 752 | + except Exception as e: |
| 753 | + raise TranslatorError(f"An exception occurred translating content for '{invoked}'.", original=e) from e |
| 754 | + |
| 755 | + new_translated = (f"/me {translated}" if me else translated).strip() |
| 756 | + return await self.channel.send_message( |
| 757 | + sender=self.bot.bot_id, |
| 758 | + message=new_translated, |
| 759 | + reply_to_message_id=self._payload.id, |
| 760 | + ) |
| 761 | + |
591 | 762 | async def send_announcement(
|
592 | 763 | self, content: str, *, color: Literal["blue", "green", "orange", "purple", "primary"] | None = None
|
593 | 764 | ) -> None:
|
|
0 commit comments