Compare commits

..
21 Commits
Author SHA1 Message Date
YoursFunny f0c287a148 fix await 2024-08-15 17:45:14 +08:00
YoursFunny 3658bfd2aa fix handler 2024-08-15 17:42:19 +08:00
YoursFunny ae1b04dee8 add extract url from message 2024-08-15 17:34:40 +08:00
YoursFunny 641f7218a4 minor fixes 2024-08-15 16:45:36 +08:00
YoursFunny 61dfa16011 fix edit message str format 2024-08-15 16:45:06 +08:00
YoursFunny 2986e80076 fix edit message 2024-08-14 01:52:01 +08:00
YoursFunny f112ecdf63 fix text 2024-08-14 01:30:08 +08:00
YoursFunny 79d50ae9a3 fix 2024-08-14 01:19:50 +08:00
YoursFunny 416331303d fix 2024-08-14 01:14:01 +08:00
YoursFunny 55771e01fe fix 2024-08-14 01:13:47 +08:00
YoursFunny afca276f49 minor fixes 2024-08-14 00:57:20 +08:00
YoursFunny 06d25854d2 fix error import 2024-08-14 00:37:01 +08:00
YoursFunny 515aa9711d new set template 2024-08-14 00:29:02 +08:00
YoursFunny 733adfc4a6 refactor using custom callback context 2024-08-13 23:20:24 +08:00
YoursFunny 03066853de refactor 2024-08-13 19:17:58 +08:00
YoursFunny 566c17a855 fix pixiv url 2024-07-19 19:34:18 +08:00
YoursFunny 87f0d16028 fix regex https pattern 2024-07-16 14:28:49 +08:00
YoursFunny 197993e522 bump version 2024-07-16 14:27:15 +08:00
YoursFunny 02abcd899e update pixiv regex 2024-07-16 14:21:43 +08:00
YoursFunny ebb48d2bb5 fix forward channel check admin user 2024-07-09 16:27:13 +08:00
YoursFunny be77a33e9d use large instead of origin for pixiv 2024-06-24 20:02:35 +08:00
7 changed files with 332 additions and 256 deletions
+102 -72
View File
@@ -4,21 +4,22 @@ import html
from functools import wraps
from typing import TYPE_CHECKING
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, MessageEntity
from telegram.constants import ChatAction, ChatType, ParseMode
from telegram.ext import (ApplicationBuilder, CallbackQueryHandler, CommandHandler, Defaults,
from telegram.ext import (ApplicationBuilder, CallbackQueryHandler, CommandHandler, ContextTypes, Defaults,
InlineQueryHandler, MessageHandler, PicklePersistence, filters)
import common
import utils.regex as regex
from utils.context import ChatData, CustomContext, EditMessage
from utils.logger import get_logger
from utils.net import NetClient
from utils.pixiv import ProcessPixiv
from utils.telegram import Telegram
if TYPE_CHECKING:
from telegram import Chat, Message, Update
from telegram.ext import Application, ContextTypes
from telegram import Message, Update
from telegram.ext import Application
logger = get_logger(__name__)
@@ -26,7 +27,7 @@ logger = get_logger(__name__)
def send_action(action):
def decorator(func):
@wraps(func)
async def command_func(update: Update, context: ContextTypes.DEFAULT_TYPE, *args, **kwargs):
async def command_func(update: Update, context: CustomContext, *args, **kwargs):
await update.effective_chat.send_action(action)
return await func(update, context, *args, **kwargs)
@@ -35,7 +36,15 @@ def send_action(action):
return decorator
async def inline_query(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
def extract_urls(message: Message) -> set[str]:
types = [MessageEntity.URL, MessageEntity.TEXT_LINK]
res = message.parse_entities(types)
res.update(message.parse_caption_entities(types))
res.update({key: key.url for key in res if key.type == MessageEntity.TEXT_LINK})
return set(res.values())
async def inline_query(update: Update, context: CustomContext) -> None:
query = update.inline_query.query
if query == "":
return
@@ -47,10 +56,10 @@ async def inline_query(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
@send_action(ChatAction.UPLOAD_PHOTO)
async def url_media(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
url = update.message.text
logger.info(f"Receiving url: {url}")
async def url_media(update: Update, context: CustomContext, url: str) -> None:
async with Telegram(url) as tweet:
if not tweet:
return
media = tweet.message_media_result()
if not media:
await update.effective_message.reply_text("No media found or media type is not supported.")
@@ -68,80 +77,94 @@ async def url_media(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if not isinstance(message_to_send, tuple):
message_to_send = (message_to_send,)
url = tweet.url
if context.user_data.get('edit_before_forward', False):
if context.chat_data.edit_before_forward:
message_reply = await update.effective_message.reply_text(
"Reply to edit message. [URL]",
reply_markup=InlineKeyboardMarkup.from_button(
InlineKeyboardButton("↩️ Confirm", callback_data="forward")
"Reply to edit message.",
reply_markup=InlineKeyboardMarkup.from_column(
[InlineKeyboardButton(name, callback_data=f"template|{name}") for name in
context.chat_data.template.keys()] + [InlineKeyboardButton("↩️ Confirm", callback_data="forward")]
),
reply_to_message_id=update.message.message_id,
)
context.user_data['message_reply'] = message_reply
context.user_data['message_to_send'] = message_to_send
context.user_data['message_url'] = url
context.chat_data.edit_message[message_reply.id] = EditMessage(
url=url,
forward=message_to_send
)
return
if 'forward_channel_id' in context.user_data:
if context.chat_data.forward_channel_id:
await forward_message(update, context, message_to_send)
async def handel_url_media(update: Update, context: CustomContext) -> None:
url = update.message.text
logger.info(f"Receiving url: {url}")
await url_media(update, context, url)
async def forward_message(
update: Update,
context: ContextTypes.DEFAULT_TYPE,
context: CustomContext,
message_to_send: tuple[Message, ...],
) -> None:
try:
await update.effective_chat.copy_messages(
context.user_data['forward_channel_id'],
context.chat_data.forward_channel_id,
[m.id for m in message_to_send]
)
except Exception as e:
await update.effective_message.reply_text(str(e))
async def edit_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if 'message_reply' not in context.user_data:
return
if update.message.reply_to_message != context.user_data['message_reply']:
return
template = context.user_data.get('template', None)
message_url = '<a href="{0}">{1}</a>'
url = context.user_data['message_url']
if template:
update_text = template.replace("[]", message_url.format(
url,
html.escape(update.message.text)
))
else:
update_text = html.escape(update.message.text)
match = regex.message_url.search(update_text)
if match:
match = match.span()
update_text = update_text[:match[0]] + message_url.format(
url,
update_text[match[0] + 1:match[1] - 1]
) + update_text[match[1]:]
message_to_send = context.user_data['message_to_send']
await message_to_send[0].edit_caption(update_text)
async def edit_message(update: Update, context: CustomContext) -> bool:
if not (reply := update.message.reply_to_message):
return False
_edit_message = context.chat_data.edit_message.get(reply.id, None)
if not _edit_message:
return False
new_text = '<a href="{0}">{1}</a>'.format(
_edit_message.url,
html.escape(update.message.text)
)
update_text = context.chat_data.template[template].replace("[]", new_text) if (
template := _edit_message.template) else new_text
await _edit_message.forward[0].edit_caption(update_text)
return True
async def query_forward_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
message_to_send = context.user_data['message_to_send']
await forward_message(update, context, message_to_send)
async def handle_message(update: Update, context: CustomContext) -> None:
if await edit_message(update, context):
return
if not (urls := extract_urls(update.message)):
return
for url in urls:
await url_media(update, context, url)
async def query_forward_message(update: Update, context: CustomContext) -> None:
_edit_message = context.chat_data.edit_message[update.effective_message.id]
await forward_message(update, context, _edit_message.forward)
await update.callback_query.answer('✅ Forwarded')
await update.callback_query.delete_message()
del context.user_data['message_reply']
del context.user_data['message_to_send']
del context.user_data['message_url']
del _edit_message
async def query_template(update: Update, context: CustomContext) -> None:
query = update.callback_query
await query.answer()
name = query.data.split("|")[1]
_edit_message = context.chat_data.edit_message[query.message.message_id]
_edit_message.template = name
await _edit_message.forward[0].edit_caption(context.chat_data.template[name])
@send_action(ChatAction.TYPING)
async def cmd_set_forward_channel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
async def cmd_set_forward_channel(update: Update, context: CustomContext) -> None:
if not context.args:
await update.effective_message.reply_text("Please provide a channel username or id.")
return
channel = context.args[0]
try:
channel: Chat = await context.bot.get_chat(channel)
channel = await context.bot.get_chat(channel)
except Exception as e:
await update.effective_message.reply_text(str(e))
return
@@ -153,55 +176,60 @@ async def cmd_set_forward_channel(update: Update, context: ContextTypes.DEFAULT_
except Exception as e:
await update.effective_message.reply_text(str(e) + "\nPlease add the bot to the channel and set as admin")
return
user = filter(lambda x: x.user.id == update.effective_user.id, channel_admin)
user = next(user, None)
if not user:
await update.effective_message.reply_text("You are not an admin of the channel.")
return
user_bot = filter(lambda x: x.user.id == context.bot.id, channel_admin)
user_bot = next(user_bot, None)
if user_bot.can_post_messages:
context.user_data['forward_channel_id'] = channel.id
context.chat_data.forward_channel_id = channel.id
await update.effective_message.reply_text("Add successfully.")
@send_action(ChatAction.TYPING)
async def cmd_remove_forward_channel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if 'forward_channel_id' in context.user_data:
del context.user_data['forward_channel_id']
async def cmd_remove_forward_channel(update: Update, context: CustomContext) -> None:
if context.chat_data.forward_channel_id:
context.chat_data.forward_channel_id = None
await update.effective_message.reply_text("Remove successfully.")
return
await update.effective_message.reply_text("No channel to remove.")
@send_action(ChatAction.TYPING)
async def cmd_edit_before_forward(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if context.user_data.get('forward_channel_id', None) is None:
async def cmd_edit_before_forward(update: Update, context: CustomContext) -> None:
if context.chat_data.forward_channel_id is None:
await update.effective_message.reply_text("Please enable forward channel first.")
return
ebf_status = context.user_data.get('edit_before_forward', False)
if ebf_status:
context.user_data['edit_before_forward'] = False
context.user_data.pop('message_reply', None)
context.user_data.pop('message_to_send', None)
context.user_data.pop('message_url', None)
if context.chat_data.edit_before_forward:
context.chat_data.edit_before_forward = False
context.chat_data.edit_message.clear()
await update.effective_message.reply_text("Disable edit before forward.")
return
context.user_data['edit_before_forward'] = True
context.chat_data.edit_before_forward = True
await update.effective_message.reply_text("Enable edit before forward.")
@send_action(ChatAction.TYPING)
async def cmd_set_template(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
async def cmd_set_template(update: Update, context: CustomContext) -> None:
reply = update.effective_message.reply_to_message
if not reply:
await update.effective_message.reply_text("Please reply to a message to set as template.")
return
if '[]' not in reply.text_html:
if '[]' not in (template := reply.text_html):
await update.effective_message.reply_text("Please reply to a message with [] to set as template.")
return
context.user_data['template'] = reply.text_html
if not context.args:
await update.effective_message.reply_text("Please provide a name for the template.")
return
context.chat_data.template[''.join(context.args)] = template
await update.effective_message.reply_text("Template set.")
@send_action(ChatAction.TYPING)
async def cmd_user_dict(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.effective_message.reply_text(str(context.user_data))
async def cmd_user_dict(update: Update, context: CustomContext) -> None:
await update.effective_message.reply_text(html.escape(str(context.chat_data)), disable_web_page_preview=True)
async def post_init(application: Application) -> None:
@@ -232,6 +260,7 @@ def main():
.token(common.BOT_TOKEN)
.defaults(defaults)
.persistence(persistence)
.context_types(ContextTypes(context=CustomContext, chat_data=ChatData))
.post_init(post_init)
.post_stop(post_stop)
.post_shutdown(post_shutdown)
@@ -246,14 +275,15 @@ def main():
handlers = [
InlineQueryHandler(inline_query),
MessageHandler((filters.Regex(regex.x_url) | filters.Regex(regex.pixiv_url)) & filters.ChatType.PRIVATE,
url_media),
handel_url_media),
CommandHandler("set_forward_channel", cmd_set_forward_channel),
CommandHandler("remove_forward_channel", cmd_remove_forward_channel),
CommandHandler("edit_before_forward", cmd_edit_before_forward),
CommandHandler("set_template", cmd_set_template),
MessageHandler(~filters.COMMAND & filters.ChatType.PRIVATE, edit_message),
MessageHandler(~filters.COMMAND & filters.ChatType.PRIVATE, handle_message),
CallbackQueryHandler(query_forward_message, pattern="forward"),
CommandHandler("bot_dict", cmd_user_dict, filters=user_filter),
CallbackQueryHandler(query_template, pattern=r"^template\|"),
CommandHandler("bot_dict", cmd_user_dict),
]
application.add_handlers(handlers)
+1 -1
View File
@@ -1,4 +1,4 @@
python-telegram-bot[webhooks]~=21.3
python-telegram-bot[webhooks]~=21.4
httpx[http2]~=0.27
uvloop~=0.19.0; sys_platform != 'win32'
async-pixiv @ git+https://github.com/TheFunny/async-pixiv@main
+42
View File
@@ -0,0 +1,42 @@
import dataclasses
from typing import Optional
from telegram import Message
from telegram.ext import Application, CallbackContext, ExtBot
@dataclasses.dataclass(repr=False)
class EditMessage:
url: str
forward: tuple[Message, ...]
template: str = ""
def __str__(self):
forward = ", ".join(f"Message({f.id})" for f in self.forward)
return f"EditMessage(url={self.url}, forward={forward}, template={self.template})"
__repr__ = __str__
class ChatData:
def __init__(self):
self.forward_channel_id: Optional[int] = None
self.edit_before_forward: bool = False
self.edit_message: dict[int, EditMessage] = {}
self.template: dict[str, str] = {}
def __str__(self):
return f"ChatData(forward_channel_id={self.forward_channel_id}, edit_before_forward={self.edit_before_forward}, " \
f"edit_message={self.edit_message}, template={self.template})"
__repr__ = __str__
class CustomContext(CallbackContext[ExtBot, dict, ChatData, dict]):
def __init__(
self,
application: Application,
chat_id: Optional[int] = None,
user_id: Optional[int] = None
):
super().__init__(application=application, chat_id=chat_id, user_id=user_id)
+6 -77
View File
@@ -1,24 +1,12 @@
from __future__ import annotations
import html
from typing import Generator, Literal, TYPE_CHECKING
from uuid import uuid4
from typing import Literal, TYPE_CHECKING
from async_pixiv import PixivClient
from async_pixiv.error import ApiError
from telegram import InlineQueryResultPhoto, InputMediaPhoto
from utils.logger import get_logger
if TYPE_CHECKING:
from async_pixiv.model.illust import Illust
from utils.types import TypeInlineQueryResult, TypeMessageMediaResult
logger = get_logger(__name__)
message_raw_text = """<a href="{url}">{text}</a> / <a href="{author_url}">{author}</a>
{tags}
"""
class PixivMedia:
@@ -110,11 +98,9 @@ class Pixiv:
]
class ProcessPixiv:
class _ProcessPixiv:
_client: PixivClient
__slots__ = ('_url', '_illust')
@classmethod
async def init_client(cls, token: str) -> None:
cls._client = PixivClient()
@@ -129,6 +115,10 @@ class ProcessPixiv:
async def refresh_token(cls):
await cls._client.login_with_token(cls._token)
class ProcessPixiv(_ProcessPixiv):
__slots__ = ('_url', '_illust')
def __init__(self, url: str):
self._url: str = url
@@ -149,64 +139,3 @@ class ProcessPixiv:
def _parse_illust_id(self) -> int:
return int(self._url.split("/")[-1])
class TelegramPixiv:
__slots__ = ('_url', '_pixiv')
def __init__(self, url: str):
self._url = url
async def __aenter__(self):
async with ProcessPixiv(self._url) as pixiv:
self._pixiv = pixiv
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
def url(self) -> str:
return self._url
@property
def message_text(self) -> str:
pixiv = self._pixiv
return message_raw_text.format(
url=pixiv.url,
author_url=pixiv.author_url,
author=html.escape(pixiv.author),
text=html.escape(pixiv.title),
tags=html.escape(" ".join(f"#{name}" for name in pixiv.tags))
)
def inline_query_result(self) -> tuple[TypeInlineQueryResult, ...]:
return tuple(self.inline_query_generator())
def message_media_result(self) -> tuple[TypeMessageMediaResult, ...]:
return tuple(self.message_media_generator())
def inline_query_generator(self) -> Generator[TypeInlineQueryResult, None, None]:
pixiv = self._pixiv
for media in pixiv.images:
logger.info(str(media))
if pixiv.type in ("illust", "manga"):
yield InlineQueryResultPhoto(
id=str(uuid4()),
photo_url=media.large,
thumbnail_url=media.thumb,
caption=self.message_text
)
else:
yield
def message_media_generator(self) -> Generator[TypeMessageMediaResult, None, None]:
pixiv = self._pixiv
for media in pixiv.images:
logger.info(str(media))
if pixiv.type in ("illust", "manga"):
yield InputMediaPhoto(
media=media.url,
has_spoiler=pixiv.is_nsfw
)
else:
yield
+3 -3
View File
@@ -1,8 +1,8 @@
import re
x_url = re.compile(r"^(?:https?://)?(?:www\.|mobile\.)?(?:x|twitter|fixvx|vxtwitter)\.com/(.+)/status/(\d+)")
x_media_url = re.compile(r"^(?:https?://)(pbs|video)\.twimg\.com/(.*)")
x_tco_url = re.compile(r"(?:https?://)t\.co/.+$", re.M)
x_media_url = re.compile(r"^(?:https?://)?(pbs|video)\.twimg\.com/(.*)")
x_tco_url = re.compile(r"(?:https?://)?t\.co/.+$", re.M)
message_url = re.compile(r"\[.+]", re.S)
pixiv_url = re.compile(r"^(?:https?://)?(?:www\.)?pixiv\.net/(?:en/)?artworks/(\d+)")
pixiv_url = re.compile(r"^(?:https?://)?(?:www\.)?pixiv\.net/(?:en/)?(?:artworks/|i/)(\d+)")
+176 -3
View File
@@ -1,9 +1,31 @@
from __future__ import annotations
import html
from functools import cached_property
from typing import Generator, TYPE_CHECKING
from uuid import uuid4
from telegram import InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo, InputMediaPhoto, \
InputMediaVideo
from common import PIXIV_REFRESH_TOKEN
from .pixiv import TelegramPixiv
from .logger import get_logger
from .pixiv import ProcessPixiv
from .regex import pixiv_url, x_url
from .tweet import TelegramTweet
from .tweet import ProcessTweet
if TYPE_CHECKING:
from .types import TypeInlineQueryResult, TypeMessageMediaResult
logger = get_logger(__name__)
message_raw_text_tweet = """{url}
<a href="{author_url}">{author}</a>: {text}
"""
message_raw_text_pixiv = """<a href="{url}">{text}</a> / <a href="{author_url}">{author}</a>
{tags}
"""
class Telegram:
@@ -18,7 +40,158 @@ class Telegram:
async with TelegramPixiv(self._url) as pixiv:
return pixiv
else:
return None
return None # TODO add raise and catch
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
class TelegramTweet:
message_raw_text = message_raw_text_tweet
__slots__ = ('_url', '_tweet', '__dict__')
def __init__(self, url: str):
self._url: str = url
async def __aenter__(self):
async with ProcessTweet(self._url) as tweet:
self._tweet = tweet
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
@cached_property
def url(self) -> str:
return self._tweet.url
@cached_property
def message_text(self) -> str:
tweet = self._tweet
return self.message_raw_text.format(
url=tweet.url,
author_url=tweet.author_url,
author=html.escape(tweet.author),
text=html.escape(tweet.text)
)
def inline_query_result(self) -> tuple[TypeInlineQueryResult, ...]:
return tuple(self.inline_query_generator())
def message_media_result(self) -> tuple[TypeMessageMediaResult, ...]:
return tuple(self.message_media_generator())
def inline_query_generator(self) -> Generator[TypeInlineQueryResult, None, None]:
tweet = self._tweet
for tweet_media in tweet.media:
logger.info(str(tweet_media))
if tweet_media.type == "image":
yield InlineQueryResultPhoto(
id=str(uuid4()),
photo_url=tweet_media.url,
thumbnail_url=tweet_media.thumb,
caption=self.message_text
)
elif tweet_media.type == "video":
yield InlineQueryResultVideo(
id=str(uuid4()),
video_url=tweet_media.url,
mime_type="video/mp4",
thumbnail_url=tweet_media.thumb,
title=tweet.text,
caption=self.message_text
)
elif tweet_media.type == "gif":
yield InlineQueryResultMpeg4Gif(
id=str(uuid4()),
mpeg4_url=tweet_media.url,
thumbnail_url=tweet_media.thumb,
caption=self.message_text
)
def message_media_generator(self) -> Generator[TypeMessageMediaResult, None, None]:
tweet = self._tweet
for tweet_media in tweet.media:
logger.info(str(tweet_media))
if tweet_media.type == "image":
yield InputMediaPhoto(
media=tweet_media.url,
has_spoiler=tweet.sensitive
)
elif tweet_media.type == "video":
yield InputMediaVideo(
media=tweet_media.url,
has_spoiler=tweet.sensitive,
thumbnail=tweet_media.thumb
)
elif tweet_media.type == "gif":
if len(tweet.media) == 1:
yield tweet_media.url, tweet.sensitive
yield InputMediaVideo(
media=tweet_media.url,
has_spoiler=tweet.sensitive,
thumbnail=tweet_media.thumb
)
class TelegramPixiv:
message_raw_text = message_raw_text_pixiv
__slots__ = ('_url', '_pixiv')
def __init__(self, url: str):
self._url = url
async def __aenter__(self):
async with ProcessPixiv(self._url) as pixiv:
self._pixiv = pixiv
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
@property
def url(self) -> str:
return self._pixiv.url
@property
def message_text(self) -> str:
pixiv = self._pixiv
return self.message_raw_text.format(
url=pixiv.url,
author_url=pixiv.author_url,
author=html.escape(pixiv.author),
text=html.escape(pixiv.title),
tags=html.escape(" ".join(f"#{name}" for name in pixiv.tags))
)
def inline_query_result(self) -> tuple[TypeInlineQueryResult, ...]:
return tuple(self.inline_query_generator())
def message_media_result(self) -> tuple[TypeMessageMediaResult, ...]:
return tuple(self.message_media_generator())
def inline_query_generator(self) -> Generator[TypeInlineQueryResult, None, None]:
pixiv = self._pixiv
for media in pixiv.images:
logger.info(str(media))
if pixiv.type in ("illust", "manga"):
yield InlineQueryResultPhoto(
id=str(uuid4()),
photo_url=media.large,
thumbnail_url=media.thumb,
caption=self.message_text
)
else:
yield
def message_media_generator(self) -> Generator[TypeMessageMediaResult, None, None]:
pixiv = self._pixiv
for media in pixiv.images:
logger.info(str(media))
if pixiv.type in ("illust", "manga"):
yield InputMediaPhoto(
media=media.large,
has_spoiler=pixiv.is_nsfw
)
else:
yield
+2 -100
View File
@@ -1,30 +1,17 @@
from __future__ import annotations
import html
from functools import cached_property
from typing import Generator, TYPE_CHECKING
from uuid import uuid4
from typing import TYPE_CHECKING
from telegram import (InlineQueryResultMpeg4Gif, InlineQueryResultPhoto, InlineQueryResultVideo, InputMediaPhoto,
InputMediaVideo)
from .logger import get_logger
from .net import NetClient
from .regex import x_media_url, x_tco_url, x_url
if TYPE_CHECKING:
from .types import TweetInfo, TypeInlineQueryResult, TypeMessageMediaResult
logger = get_logger(__name__)
from .types import TweetInfo
twimg_url = 'https://pbs.twimg.com/'
vx_api_url = 'https://api.vxtwitter.com/{0}/status/{1}'
message_raw_text = """{url}
<a href="{author_url}">{author}</a>: {text}
"""
class TweetMedia:
__slots__ = ('_url', '_thumb', '_type', '__dict__')
@@ -163,88 +150,3 @@ class ProcessTweet:
]
class TelegramTweet:
__slots__ = ('_url', '_tweet', '__dict__')
def __init__(self, url: str):
self._url: str = url
async def __aenter__(self):
async with ProcessTweet(self._url) as tweet:
self._tweet = tweet
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
@cached_property
def url(self) -> str:
return self._tweet.url
@cached_property
def message_text(self) -> str:
tweet = self._tweet
return message_raw_text.format(
url=tweet.url,
author_url=tweet.author_url,
author=html.escape(tweet.author),
text=html.escape(tweet.text)
)
def inline_query_result(self) -> tuple[TypeInlineQueryResult, ...]:
return tuple(self.inline_query_generator())
def message_media_result(self) -> tuple[TypeMessageMediaResult, ...]:
return tuple(self.message_media_generator())
def inline_query_generator(self) -> Generator[TypeInlineQueryResult, None, None]:
tweet = self._tweet
for tweet_media in tweet.media:
logger.info(str(tweet_media))
if tweet_media.type == "image":
yield InlineQueryResultPhoto(
id=str(uuid4()),
photo_url=tweet_media.url,
thumbnail_url=tweet_media.thumb,
caption=self.message_text
)
elif tweet_media.type == "video":
yield InlineQueryResultVideo(
id=str(uuid4()),
video_url=tweet_media.url,
mime_type="video/mp4",
thumbnail_url=tweet_media.thumb,
title=tweet.text,
caption=self.message_text
)
elif tweet_media.type == "gif":
yield InlineQueryResultMpeg4Gif(
id=str(uuid4()),
mpeg4_url=tweet_media.url,
thumbnail_url=tweet_media.thumb,
caption=self.message_text
)
def message_media_generator(self) -> Generator[TypeMessageMediaResult, None, None]:
tweet = self._tweet
for tweet_media in tweet.media:
logger.info(str(tweet_media))
if tweet_media.type == "image":
yield InputMediaPhoto(
media=tweet_media.url,
has_spoiler=tweet.sensitive
)
elif tweet_media.type == "video":
yield InputMediaVideo(
media=tweet_media.url,
has_spoiler=tweet.sensitive,
thumbnail=tweet_media.thumb
)
elif tweet_media.type == "gif":
if len(tweet.media) == 1:
yield tweet_media.url, tweet.sensitive
yield InputMediaVideo(
media=tweet_media.url,
has_spoiler=tweet.sensitive,
thumbnail=tweet_media.thumb
)