Skip to content

Commit

Permalink
Parsing structure progress
Browse files Browse the repository at this point in the history
  • Loading branch information
asaf-kali committed Nov 8, 2024
1 parent a515a5a commit 337e642
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 106 deletions.
Empty file.
36 changes: 36 additions & 0 deletions app/bot/handlers/parse/parse_board_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import requests
from bot.config import get_config
from bot.handlers.other.event_handler import EventHandler, build_board_keyboard
from bot.handlers.parse.photos import _get_base64_photo
from bot.models import BotState
from codenames.game.board import Board
from codenames.game.card import Card

# Board -> Fixing


class ParseBoardHandler(EventHandler):
def handle(self):
photo_base64 = _get_base64_photo(photos=self.update.message.photo)
self.send_text("Working on it. This might take a minute ⏳️")
parsing_state = self.session.parsing_state
parsed_words = _parse_board_words(photo_base64=photo_base64, language=parsing_state.language)
words = [word if word else str(i) for i, word in enumerate(parsed_words)]
cards = [Card(word=word, color=color) for word, color in zip(words, parsing_state.card_colors)]
parsed_board = Board(language=parsing_state.language, cards=cards)
keyboard = build_board_keyboard(table=parsed_board.as_table, is_game_over=True)
message = "🎉 Done! Here's the board.\nClick on any card to fix it. When you are done, send me /done."
text = self.send_markdown(text=message, reply_markup=keyboard)
self.update_session(last_keyboard_message_id=text.message_id, parsing_state=None)
return BotState.PARSE_FIXES


def _parse_board_words(photo_base64: str, language: str) -> list[str]:
env_config = get_config()
url = f"{env_config.base_parser_url}/parse-board"
payload = {"board_image_b64": photo_base64, "language": language}
response = requests.get(url=url, json=payload, timeout=80)
response.raise_for_status()
response_json = response.json()
words = response_json.get("words")
return words
13 changes: 13 additions & 0 deletions app/bot/handlers/parse/parse_fixing_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from bot.handlers.other.event_handler import EventHandler
from bot.models import BotState

# Fixing


class ParseFixesHandler(EventHandler):
def handle(self):
text = self.update.message.text.lower().strip()
if text == "/done":
return
self.send_text("🧩 Please send me a picture of the fixed board:")
return BotState.PARSE_BOARD
28 changes: 28 additions & 0 deletions app/bot/handlers/parse/parse_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from bot.handlers.other.event_handler import EventHandler
from bot.models import BotState, ParsingState
from the_spymaster_util.logger import get_logger

log = get_logger(__name__)

# Entrypoint -> Language

LANGUAGES_CODES = {
"hebrew": "heb",
"hnglish": "eng",
"hussian": "rus",
}


class ParseHandler(EventHandler):
def handle(self):
keyboard = _build_language_options_keyboard()
parsing_state = ParsingState()
self.update_session(parsing_state=parsing_state)
# Language selection
self.send_markdown("🔤 Pick cards language:", reply_markup=keyboard)
return BotState.PARSE_LANGUAGE


def _build_language_options_keyboard() -> list[str]:
keyboard = [language.title() for language in LANGUAGES_CODES.keys()]
return keyboard
25 changes: 25 additions & 0 deletions app/bot/handlers/parse/parse_language_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from bot.handlers.other.event_handler import EventHandler
from bot.handlers.parse.parse_handler import LANGUAGES_CODES, log
from bot.models import BotState

# Language -> Map


class ParseLanguageHandler(EventHandler):
def handle(self):
text = self.update.message.text.lower()
language_code = _get_language_code(text)
log.info(f"Setting language: '{language_code}'")
self.update_parsing_state(language=language_code)
# Map parsing
self.send_text("🗺️ Please send me a picture of the map:")
return BotState.PARSE_MAP


def _get_language_code(text: str) -> str:
text = text.lower().strip()
if text in LANGUAGES_CODES:
return LANGUAGES_CODES[text]
log.info(f"Unknown language: '{text}'")
language_code = text[:3]
return language_code
40 changes: 40 additions & 0 deletions app/bot/handlers/parse/parse_map_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import requests
from bot.config import get_config
from bot.handlers.other.event_handler import EventHandler
from bot.handlers.parse.photos import _get_base64_photo
from bot.models import BotState, ParsingState
from codenames.game.color import CardColor


# Map -> Board
class ParseMapHandler(EventHandler):
def handle(self):
photo_base64 = _get_base64_photo(photos=self.update.message.photo)
card_colors = _parse_map_colors(photo_base64)
self._send_as_emoji_table(card_colors)
parsing_state = ParsingState(language="heb", card_colors=card_colors)
self.update_session(parsing_state=parsing_state)
# Board parsing
self.send_text("🧩 Please send me a picture of the board:")
return BotState.PARSE_BOARD

def _send_as_emoji_table(self, card_colors: list[CardColor]):
result = "I got: \n\n"
for i in range(0, len(card_colors), 5):
row = card_colors[i : i + 5]
row_emojis = " ".join(card.emoji for card in row)
result += f"{row_emojis}\n"
result += "\nYou will have a chance to fix any mistakes later."
self.send_text(result)


def _parse_map_colors(photo_base64: str) -> list[CardColor]:
env_config = get_config()
url = f"{env_config.base_parser_url}/parse-color-map"
payload = {"map_image_b64": photo_base64}
response = requests.get(url=url, json=payload, timeout=15)
response.raise_for_status()
response_json = response.json()
map_colors = response_json.get("map_colors")
card_colors = [CardColor(color) for color in map_colors]
return card_colors
21 changes: 21 additions & 0 deletions app/bot/handlers/parse/photos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import base64

from bot.handlers.parse.parse_handler import log
from bot.models import BadMessageError
from telegram import PhotoSize


def _get_base64_photo(photos: list[PhotoSize]) -> str:
if not photos:
raise BadMessageError("No photo found in message")
log.info(f"Got {len(photos)} photos, downloading the largest one")
photo_meta = _pick_largest_photo(photos)
photo_ptr = photo_meta.get_file()
photo_bytes = photo_ptr.download_as_bytearray()
photo_base64 = base64.b64encode(photo_bytes).decode("utf-8")
log.info("Downloaded and encoded photo")
return photo_base64


def _pick_largest_photo(photos: list[PhotoSize]) -> PhotoSize:
return max(photos, key=lambda photo: photo.file_size)
106 changes: 0 additions & 106 deletions app/bot/handlers/parse_handler.py

This file was deleted.

0 comments on commit 337e642

Please sign in to comment.