Add support for playing games. Fixes #256
This commit is contained in:
@@ -14,17 +14,21 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from typing import Awaitable, Dict, List, Optional, Tuple
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import base64
|
||||
import re
|
||||
|
||||
from telethon.errors import (
|
||||
InviteHashInvalidError, InviteHashExpiredError, UserAlreadyParticipantError)
|
||||
from telethon.tl.types import User as TLUser
|
||||
from telethon.tl.types import TypeUpdates
|
||||
from telethon.tl.functions.messages import ImportChatInviteRequest, CheckChatInviteRequest
|
||||
from telethon.errors import (InviteHashInvalidError, InviteHashExpiredError,
|
||||
UserAlreadyParticipantError)
|
||||
from telethon.tl.types import User as TLUser, TypeUpdates, MessageMediaGame, PeerChat
|
||||
from telethon.tl.types.messages import BotCallbackAnswer
|
||||
from telethon.tl.functions.messages import (ImportChatInviteRequest, CheckChatInviteRequest,
|
||||
GetBotCallbackAnswerRequest)
|
||||
from telethon.tl.functions.channels import JoinChannelRequest
|
||||
|
||||
from .. import puppet as pu, portal as po
|
||||
from ..db import Message as DBMessage
|
||||
from ..types import TelegramID
|
||||
from . import command_handler, CommandEvent, SECTION_MISC, SECTION_CREATING_PORTALS
|
||||
|
||||
|
||||
@@ -158,3 +162,54 @@ async def sync(evt: CommandEvent) -> Optional[Dict]:
|
||||
if not sync_only or sync_only == "me":
|
||||
await evt.sender.update_info()
|
||||
return await evt.reply("Synchronization complete.")
|
||||
|
||||
|
||||
@command_handler(help_section=SECTION_MISC,
|
||||
help_args="<play ID>",
|
||||
help_text="Play a Telegram game")
|
||||
async def play(evt: CommandEvent) -> Optional[Dict]:
|
||||
if len(evt.args) < 1:
|
||||
return await evt.reply("**Usage:** `$cmdprefix+sp play <play ID>`")
|
||||
elif not await evt.sender.is_logged_in():
|
||||
return await evt.reply("You must be logged in with a real account to play games.")
|
||||
elif evt.sender.is_bot:
|
||||
return await evt.reply("Bots can't play games :(")
|
||||
|
||||
try:
|
||||
space = None
|
||||
peer_type, play_id = base64.b64decode(evt.args[0]).decode("utf-8").split("-", 1)
|
||||
if peer_type == "chan" or peer_type == "user":
|
||||
tgid, msg_id = play_id.split("-")
|
||||
elif peer_type == "chat":
|
||||
tgid, space, msg_id = play_id.split("-")
|
||||
space = TelegramID(int(space))
|
||||
else:
|
||||
raise ValueError()
|
||||
tgid = TelegramID(int(tgid))
|
||||
msg_id = TelegramID(int(msg_id))
|
||||
except ValueError:
|
||||
return await evt.reply("Invalid play ID (format)")
|
||||
|
||||
if peer_type == "chat":
|
||||
orig_msg = DBMessage.get_by_tgid(msg_id, space)
|
||||
if not orig_msg:
|
||||
return await evt.reply("Invalid play ID (original message not found in db)")
|
||||
new_msg = DBMessage.get_by_mxid(orig_msg.mxid, orig_msg.mx_room, evt.sender.tgid)
|
||||
if not new_msg:
|
||||
return await evt.reply("Invalid play ID (your copy of message not found in db)")
|
||||
msg_id = new_msg.tgid
|
||||
try:
|
||||
peer = await evt.sender.client.get_input_entity(tgid)
|
||||
except ValueError as e:
|
||||
return await evt.reply("Invalid play ID (chat not found)")
|
||||
|
||||
msg = await evt.sender.client.get_messages(entity=peer, ids=msg_id)
|
||||
if not msg or not isinstance(msg.media, MessageMediaGame):
|
||||
return await evt.reply("Invalid play ID (message doesn't look like a game)")
|
||||
|
||||
game = await evt.sender.client(GetBotCallbackAnswerRequest(peer=peer, msg_id=msg_id, game=True))
|
||||
if not isinstance(game, BotCallbackAnswer):
|
||||
return await evt.reply("Game request response invalid")
|
||||
|
||||
await evt.reply(f"Click [here]({game.url}) to play {msg.media.game.title}:\n\n"
|
||||
f"{msg.media.game.description}")
|
||||
|
||||
@@ -179,9 +179,12 @@ async def _add_reply_header(source: "AbstractUser", text: str, html: str, evt: M
|
||||
async def telegram_to_matrix(evt: Message, source: "AbstractUser",
|
||||
main_intent: Optional[IntentAPI] = None,
|
||||
is_edit: bool = False, prefix_text: Optional[str] = None,
|
||||
prefix_html: Optional[str] = None) -> Tuple[str, str, Dict]:
|
||||
text = add_surrogates(evt.message)
|
||||
html = _telegram_entities_to_matrix_catch(text, evt.entities) if evt.entities else None
|
||||
prefix_html: Optional[str] = None, override_text: str = None,
|
||||
override_entities: List[TypeMessageEntity] = None
|
||||
) -> Tuple[str, str, Dict]:
|
||||
text = add_surrogates(override_text or evt.message)
|
||||
entities = override_entities or evt.entities
|
||||
html = _telegram_entities_to_matrix_catch(text, entities) if entities else None
|
||||
relates_to = {} # type: Dict
|
||||
|
||||
if prefix_html:
|
||||
|
||||
+40
-11
@@ -23,6 +23,7 @@ import asyncio
|
||||
import random
|
||||
import mimetypes
|
||||
import unicodedata
|
||||
import base64
|
||||
import hashlib
|
||||
import logging
|
||||
import json
|
||||
@@ -55,12 +56,12 @@ from telethon.tl.types import (
|
||||
MessageActionChatDeletePhoto, MessageActionChatDeleteUser, MessageActionChatEditPhoto,
|
||||
MessageActionChatEditTitle, MessageActionChatJoinedByLink, MessageActionChatMigrateTo,
|
||||
MessageActionPinMessage, MessageMediaContact, MessageMediaDocument, MessageMediaGeo,
|
||||
MessageMediaPhoto, MessageMediaUnsupported, MessageService, PeerChannel, PeerChat, PeerUser,
|
||||
Photo, PhotoCachedSize, SendMessageCancelAction, SendMessageTypingAction,
|
||||
MessageMediaPhoto, MessageMediaUnsupported, MessageMediaGame, MessageService, PeerChannel,
|
||||
PeerChat, PeerUser, Photo, PhotoCachedSize, SendMessageCancelAction, SendMessageTypingAction,
|
||||
TypeChannelParticipant, TypeChat, TypeChatParticipant, TypeDocumentAttribute, TypeInputPeer,
|
||||
TypeMessageAction, TypeMessageEntity, TypePeer, TypePhotoSize, TypeUpdates, TypeUser,
|
||||
TypeUserFull, UpdateChatUserTyping, UpdateNewChannelMessage, UpdateNewMessage, UpdateUserTyping,
|
||||
User, UserFull)
|
||||
User, UserFull, MessageEntityCode)
|
||||
from mautrix_appservice import MatrixRequestError, IntentError, AppService, IntentAPI
|
||||
|
||||
from .types import MatrixEventID, MatrixRoomID, MatrixUserID, TelegramID
|
||||
@@ -1414,13 +1415,11 @@ class Portal:
|
||||
|
||||
async def handle_telegram_unsupported(self, source: 'AbstractUser', intent: IntentAPI,
|
||||
evt: Message, relates_to: dict = None) -> dict:
|
||||
prev = evt.message
|
||||
evt.message = ("This message is not supported on your version of Mautrix-Telegram. "
|
||||
"Please check https://github.com/tulir/mautrix-telegram or ask your "
|
||||
"bridge administrator about possible updates.")
|
||||
text, html, relates_to = await formatter.telegram_to_matrix(evt, source,
|
||||
self.main_intent)
|
||||
evt.message = prev
|
||||
override_text = ("This message is not supported on your version of Mautrix-Telegram. "
|
||||
"Please check https://github.com/tulir/mautrix-telegram or ask your "
|
||||
"bridge administrator about possible updates.")
|
||||
text, html, relates_to = await formatter.telegram_to_matrix(
|
||||
evt, source, self.main_intent, override_text=override_text)
|
||||
await intent.set_typing(self.mxid, is_typing=False)
|
||||
return await intent.send_message(self.mxid, {
|
||||
"body": text,
|
||||
@@ -1431,6 +1430,35 @@ class Portal:
|
||||
"net.maunium.telegram.unsupported": True,
|
||||
}, timestamp=evt.date, external_url=self.get_external_url(evt))
|
||||
|
||||
async def handle_telegram_game(self, source: 'AbstractUser', intent: IntentAPI,
|
||||
evt: Message, relates_to: dict = None):
|
||||
game = evt.media.game
|
||||
if self.peer_type == "channel":
|
||||
play_id = base64.b64encode(f"chan-{self.tgid}-{evt.id}".encode("utf-8"))
|
||||
elif self.peer_type == "chat":
|
||||
play_id = base64.b64encode(f"chat-{self.tgid}-{source.tgid}-{evt.id}".encode("utf-8"))
|
||||
elif self.peer_type == "user":
|
||||
play_id = base64.b64encode(f"user-{self.tgid}-{evt.id}".encode("utf-8"))
|
||||
else:
|
||||
raise ValueError("Portal has invalid peer type")
|
||||
play_id = play_id.decode("utf-8")
|
||||
command = f"!tg play {play_id}"
|
||||
override_text = (f"Run {command} in your bridge management room to "
|
||||
f"play {game.title}:\n\n{game.description}")
|
||||
override_entities = [MessageEntityCode(offset=len("Run "), length=len(command))]
|
||||
text, html, relates_to = await formatter.telegram_to_matrix(
|
||||
evt, source, self.main_intent,
|
||||
override_text=override_text, override_entities=override_entities)
|
||||
await intent.set_typing(self.mxid, is_typing=False)
|
||||
return await intent.send_message(self.mxid, {
|
||||
"body": text,
|
||||
"msgtype": "m.notice",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": html,
|
||||
"m.relates_to": relates_to,
|
||||
"net.maunium.telegram.game": play_id,
|
||||
}, timestamp=evt.date, external_url=self.get_external_url(evt))
|
||||
|
||||
async def handle_telegram_edit(self, source: 'AbstractUser', sender: p.Puppet,
|
||||
evt: Message) -> None:
|
||||
if not self.mxid:
|
||||
@@ -1514,7 +1542,7 @@ class Portal:
|
||||
entity = await source.client.get_entity(PeerUser(sender.tgid))
|
||||
await sender.update_info(source, entity)
|
||||
|
||||
allowed_media = (MessageMediaPhoto, MessageMediaDocument, MessageMediaGeo,
|
||||
allowed_media = (MessageMediaPhoto, MessageMediaDocument, MessageMediaGeo, MessageMediaGame,
|
||||
MessageMediaUnsupported)
|
||||
media = evt.media if hasattr(evt, "media") and isinstance(evt.media,
|
||||
allowed_media) else None
|
||||
@@ -1528,6 +1556,7 @@ class Portal:
|
||||
MessageMediaDocument: self.handle_telegram_document,
|
||||
MessageMediaGeo: self.handle_telegram_location,
|
||||
MessageMediaUnsupported: self.handle_telegram_unsupported,
|
||||
MessageMediaGame: self.handle_telegram_game,
|
||||
}[type(media)](source, intent, evt,
|
||||
relates_to=formatter.telegram_reply_to_matrix(evt, source))
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user