
    soj-_                        d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZm	Z	m
Z
 d dlmZ d dlmZ d dlmZ d dlmZmZmZmZmZmZmZmZmZmZ d dlZd dlmZ d d	lm Z  d d
l!m"Z" d dl#m$Z$ d dl%m&Z&m'Z' d dl(m)Z) d dl*m+Z+  ej,        d          Z-dZ.ej/        0                    ej1        d          dfej/        0                    ej1        d          dfdZ2ej/        0                    ej1        d          Z3ddddddddZ4de5d e5dz  fd!Z6d"e5d e7e8e5e5dz  f                  fd#Z9d"e5d ej        fd$Z:d%e5d e5dz  fd&Z;de5d e5fd'Z<	 d7d(ed)e"d e5fd*Z=d)e"d+e5d,e5d-e5dz  d.e5dz  d/e5d0e5d e5fd1Z>d2e d e?fd3Z@d8d5eAd dfd6ZBdS )9    N)confusables)Image	ImageDraw	ImageFont)features)Update)	unidecode)
WantedPosterVerticalAlignmentCaptureConditionEffectStampBOUNTY_POSTER_NAME_TEXTURE_PATHBOUNTY_POSTER_NAME_FONT_SIZEBOUNTY_POSTER_NAME_MAX_WBOUNTY_POSTER_NAME_HBOUNTY_POSTER_NAME_START_Y)Leaderboard)LeaderboardUser)User)BossType)LeaderboardRankget_rank_by_index)user_has_eaten_devil_fruit)generate_temp_file_pathraqmUnknownzNotoSansArabic-Bold.ttfrtlzNotoSansDevanagari-Bold.ttfltr)ARABIC
DEVANAGARIzBlogger_Sans-Bold.otfRCLEHWM)u   ᖇu   ᑢu   ᒪu   ᘿu   Ꮋu   Ꮿu   Ꮇnamereturnc                     | D ]v}|                                 s|                                s+	 t          j        |          }n# t          $ r Y Mw xY wt
          D ]}|                    |          r|c c S wdS )a  
    Detects whether a name is written in a script we can show as-is on the poster (see
    NATIVE_SCRIPT_FONT_MAP), by checking each character's Unicode name (e.g. a character named
    "ARABIC LETTER SEEN" belongs to the "ARABIC" script)
    :param name: The raw name, as it is on Telegram
    :return: The script name (a key of NATIVE_SCRIPT_FONT_MAP) if found, otherwise None
    N)isspaceisalphaunicodedatar)   
ValueErrorNATIVE_SCRIPT_FONT_MAP
startswith)r)   char	char_namescripts       )/app/src/service/bounty_poster_service.pyget_native_scriptr6   V   s       <<>> 	 		#(..II 	 	 	H	 - 	 	F##F++ 	 4s   A
AAtextc                    g }d}d}| D ]}d}t          j        |                              d          r|}n_|                                rK	 t          j        |          }n# t
          $ r d}Y nw xY wt          D ]}|                    |          r|} n||k    r||z  }|r|                    ||f           |}|}|r|                    ||f           |S )uT  
    Segments text into contiguous runs of a single native script (see NATIVE_SCRIPT_FONT_MAP) or
    plain Latin/neutral characters (Latin letters, digits, punctuation, spaces), so a name that
    mixes scripts (e.g. "apple سیب FARZIN") can have each part rendered with a font that actually
    supports it, instead of forcing the whole string through a single script's font.
    :param text: The text to segment
    :return: A list of (segment_text, script) tuples in original order. script is a key of
    NATIVE_SCRIPT_FONT_MAP for a native-script run, or None for a Latin/neutral run
     Nr(   )r.   categoryr1   r-   r)   r/   r0   append)r7   runscurrent_textcurrent_scriptr2   char_scriptr3   r4   s           r5   _segment_by_scriptr@   o   s:    *,DL!%N ) )%%0055 	 )KK\\^^ 		',T22		   			 1  ''// "(KE .((D LL <\>:;;;L(NN 4\>2333Ks   A!!A0/A0c           	         t          |           }i |D ]q\  }}|v r
|%t          j        t          t                    df|<   1t
          |         \  }}t          j        |t          t          j        j                  |f|<   rt          j	        t                    }|j        \  }}t          j        d||f          }	t          j        |	          fd|D             }
t          |
          }d}|t           k    r4t#          ||z  t           z            }t          j        d||f          }d}nt          j        d||f          }t          j        |          }|j        d         |z
  d	z  }t%          ||
          D ]@\  \  }}}|         \  }}|                    ||d	z  z   t(          f||d
d|           ||z  }A|r|                    ||f          }|                    |           |S )u  
    Builds the name texture component for a name that mixes native-script text (see
    NATIVE_SCRIPT_FONT_MAP) with Latin/neutral text (e.g. "apple سیب FARZIN"), rendering each
    part with a font that actually supports it, positioned side by side in their original
    order. Without this, the whole string would be forced through a single native-script font
    (e.g. NotoSansArabic), which doesn't cover Latin characters -- those would show up as
    unsupported-glyph placeholder boxes instead of actual letters.

    Uses a texture + alpha mask, auto-scaling if the combined text is too wide, but draws each
    script run with its own font/direction instead of a single one for the whole string.
    :param text: The full name text, in its original (possibly mixed) script
    :return: The name component image, ready to paste over the poster at
    (0, BOUNTY_POSTER_NAME_START_Y)
    Nr   )layout_enginer$   c                 v    g | ]5\  }}                     ||         d          |         d                   6S )r      )font	direction)
textlength).0run_textr4   fonts_and_directionsmeasuring_draws      r5   
<listcomp>z6_build_mixed_script_name_component.<locals>.<listcomp>   sa        Hf 	!!%f-a0*6215 	" 	
 	
      FTr      whitems)rE   fillanchorrF   )r@   r   truetypeLATIN_NAME_FONT_PATHr   r0   LayoutRAQMr   openr   sizenewr   Drawsumr   intzipr7   r   resizeputalpha)r7   r<   _r4   	font_pathrF   texture_backgroundtexture_background_wtexture_background_hmeasuring_alpha
run_widthstotal_wshould_scalenew_walphadraw	current_xrI   run_wrE   rJ   rK   s                       @@r5   "_build_mixed_script_name_componentrn      so     d##DQS  	6)))>"#79UVV, ((
 $:&#A Iy";9K[K`   	, (( $CDD1C1H.. i&:<P%QRRO^O44N     !%  J *ooGL)))W337OOPP	#';<==	# 46JKLL>%  DA(A-I%(z%:%: 
 
!6E.v6i		"$89 	 	
 	
 	
 	U		 K24HIJJ&&&rM   r2   c                 $   t          j        | d          }|sdS |D ]r}|                    dg           D ]Y}|                    dd          }t          |          dk    r.|                                r|                                r|c c S ZsdS )aY  
    General-purpose fallback for FANCY_FONT_LOOKALIKE_CHAR_MAP, for "fancy font" look-alike
    characters that haven't been reported/added there yet. Looks up Unicode's own official
    "confusables" data (the same dataset used for anti-phishing/anti-spoofing detection, listing
    characters from any script that are visually confusable with one another) for a plain ASCII
    letter/digit that this character could be mistaken for. Only called for non-ASCII characters,
    since otherwise this would find characters that look like an existing ASCII letter and
    "correct" the letter into a different one (e.g. it considers "l" a look-alike of "1", which
    would wrongly replace a legitimate "1" with "l")
    :param char: A single non-ASCII character
    :return: A single ASCII letter/digit if a confident look-alike is found, otherwise None
    T)greedyN
homoglyphscr9   rD   )r   is_confusablegetlenisasciiisalnum)r2   matchesmatch	homoglyph	candidates        r5   _get_homoglyph_latin_letterr|      s     'T:::G t ! !<44 	! 	!I!c2..I9~~""y'8'8':':"y?P?P?R?R"      	!
 4rM   c                 >   | sdS g }| D ]}|                                 r|                    d           -|t          v r!|                    t          |                    Wt          |                                          }t          |          dk    r*|                                r|                    |           |                                s't          |          }||                    |           |r)|                                r|                    |           t          j
        ddd                    |                                                    }t          j
        dd|          }t          j
        dd|                                          S )u3  
    Sanitizes a name for use on a wanted poster, for scripts that aren't shown as-is (see
    NATIVE_SCRIPT_FONT_MAP / get_native_script).

    The poster font only supports latin characters, so names are normally transliterated
    with unidecode. The problem is that unidecode doesn't just transliterate, it also
    *describes* characters it has no real letter equivalent for (e.g. the chess piece "♚"
    becomes the phrase "black king", an emoji becomes a description, etc.). Those
    descriptions would otherwise be added to the name, making it longer than it should be
    and, in the worst case, pushing it past the point where it gets discarded entirely,
    resulting in a blank name on the poster.

    To support as many characters as possible while avoiding that, every character is
    transliterated individually and only kept if the result is a clean word with no
    spaces in it (e.g. "Ⓐ" -> "A", "Ω" -> "O", "你" -> "Ni", "Ⅷ" -> "VIII" are all kept).
    Characters whose transliteration is a description made up of multiple words (e.g.
    "♚" -> "black king") or that have no latin equivalent at all (most emoji) are
    discarded instead of being spelled out.

    Known "fancy font" look-alike characters (see FANCY_FONT_LOOKALIKE_CHAR_MAP) are mapped
    to their intended Latin letter directly, bypassing unidecode, since unidecode would
    otherwise transliterate them based on their unrelated real meaning instead of their
    intended visual appearance. Any other non-ASCII look-alike not in that map is checked
    against Unicode's official confusables data (see _get_homoglyph_latin_letter) as a general
    safety net, before finally falling back to unidecode
    :param name: The raw name to sanitize, as it is on Telegram
    :return: The sanitized name, safe to pass to the poster generator
    r9    rD   Nz\s+z[^A-Za-z0-9 '\-.])r,   r;   FANCY_FONT_LOOKALIKE_CHAR_MAPr	   stripru   rw   rv   r|   resubjoin)r)   
kept_charsr2   transliteratedrz   cleaneds         r5   get_bounty_poster_namer     s   <  rJ &. &.<<>> 	c"""000;DABBB"4..00 ~!##(>(>(@(@#n--- ||~~ 	3D99I$!!),,,  	.n4466 	.n---fVS"''*"5"566<<>>G f)2w77G6&#w''--///rM   updateuserc                   K   ddl m} |j        pd                                }|j        pd                                }d}t
          rt          |          pt          |          }|r| d|                                 nd}|rd}d}	n)t          |          }t          |          }	|s	|	st          } || |           d{V }
t          j
        t          ||
||	||||	  	         d{V S )z
    Gets the bounty poster of a user
    :param update: Telegram update
    :param user: The user to get the poster of
    :param telegram_user: Optional Telegram user to fetch the portrait for
    :return: The path to the poster
    r   )get_user_profile_photor9   Nr~   )src.service.user_servicer   tg_first_namer   tg_last_nameRAQM_AVAILABLEr6   r   BOUNTY_POSTER_FALLBACK_NAMEasyncio	to_thread_generate_poster_sync)r   r   telegram_userr   raw_first_nameraw_last_namenative_scriptnative_script_textposter_first_nameposter_last_nameportraits              r5   get_bounty_posterr   b  sb      @?????(.B5577N&,"3355M M ^).99]=N}=]=]HU_N<<]<<BBDDD[_ < 3>BB1-@@ ! 	<)9 	< ;++FMBBBBBBBBH"
 
 
 
 
 
 
 
 
rM   r   r   r   r   r   r   c           	         ddl m} t          |||| j                  }	t          j        }
g }d}t          |           rt          j        }
 ||           }|dt          j	        fvrt          j
        }
|xt          j	        k    r- |                    t          j                   t          j	        }n}xt          j        k    rn$xt          j        k    rnxt          j        k    rn nH |                    t          j                   |t          j        u rt          j        }nt          j        }	 |	                    t-          t.          j                  t2          j        |
||d          }|r	 t7          |          }t9          j        |                              d          }|                    |dt@          f|           |!                    |           n# tD          $ r}tG          j$        d|            tK          |          }tK          |          }|s	|stL          }t          |	j'        ||| j                  }|                    |t2          j        |
||d          }Y d}~nd}~ww xY w|S )a  
    Does the actual CPU-bound poster generation (font loading/shaping, image compositing,
    resizing, disk I/O) synchronously. Only ever called through asyncio.to_thread from
    get_bounty_poster, so this blocking work runs on a separate thread instead of stalling the
    single-threaded event loop (and with it, every other scheduled task in the Bot - e.g. a
    game's start countdown - for however long generation takes) while it runs
    :return: The path to the generated poster
    r   )get_boss_type)r   
first_name	last_namebountyN)output_poster_pathportrait_vertical_aligncapture_conditioneffectsstampfull_name_max_lengthRGBz<Native script name rendering failed, falling back to Latin: )(r   r   r
   r   r   DEAD_OR_ALIVEr   
ONLY_ALIVEr   WARLORD	ONLY_DEADr;   r   FROSTr   ADMINPIRATE_KINGLEGENDARY_PIRATE	LIGHTNINGFLEE_ON_SIGHTDO_NOT_ENGAGEgenerater   rr   BOUNTY_POSTER_EXTENSIONr   TOPrn   r   rW   convertpaster   save	Exceptionloggingerrorr   r   r   )r   r   r   r   r   r   r   r   r   wanted_posterr   r   r   	boss_typeposter_pathname_componentposter_imageefallback_first_namefallback_last_namefallback_posters                        r5   r   r     s   & 766666 ##{	  M +;*HGE!$'' 8,7'---Ix/000,6 XNN6<(((MEE X^^^^2h22225NX5N5N5N5N5N5N5NNN6+,,, HN**+ +((213LMM 1 5+
 " )  K  	?@RSSN :k22::5AAL~3M/NP^___k**** 	 	 	 M\YZ\\]]]"8"H"H!7!F!F& B/A B&A#*&/--{	  O *22#.(9(="3%) 3  KKKKKK!	2 s   ,A)G 
I, BI''I,leaderboard_userc                 8    t          | j                  }|j        S )z
    Gets the bounty poster limit of a user by their leaderboard position
    :param leaderboard_user: The user to get the bounty poster limit of
    :return: The bounty poster limit of the user
    )r   
rank_indexbounty_poster_limit)r   leaderboard_ranks     r5   get_bounty_poster_limitr     s     )::J:U(V(V//rM   Freset_previous_leaderboardc                 H  K   ddl m} | r |d          }t          j        t          j                                      t          j        |k              }|Wt          j        d                              t          j	        
                    |                                                      |            }|f|j        D ]`}|}t          j        t          |                                        t          j	        |j        k                                               _dS dS )z
    Resets the bounty poster limit

    :param reset_previous_leaderboard: If to reset the limit for users if the previous leaderboard
    r   )get_leaderboardrD   N)r   )src.service.leaderboard_servicer   r   selectr   whereleaderboardr   r   idin_executeleaderboard_usersr   )r   r   previous_leaderboardprevious_leaderboard_users_idcurrent_leaderboardr   s         r5   reset_bounty_poster_limitr     s.      @?????! 	,;OA,>,>(7(>?S(T(T(Z(Z'+??)
 )
%  +KA...449:: giii (7'8'8& 3 E 	 	0@K,CDT,U,UVVV\\+00 giiii '&	 	rM   )N)F)Cr   r   osr   r.   confusable_homoglyphsr   PILr   r   r   r   pil_featuressrc.tg_compatr   r	   wantedposter.wantedposterr
   r   r   r   r   r   r   r   r   r   	constantsrr   src.model.Leaderboardr   src.model.LeaderboardUserr   src.model.Userr   src.model.enums.BossTyper   src.model.enums.LeaderboardRankr   r   src.service.devil_fruit_servicer   src.utils.download_utilsr   checkr   r   pathr   ASSETS_FONTS_DIRr0   rT   r   strr6   listtupler@   rn   r|   r   r   r   r\   r   boolr    rM   r5   <module>r      s      				 				     - - - - - - + + + + + + + + + + ( ( ( ( ( (                                              - - - - - - 5 5 5 5 5 5       - - - - - - N N N N N N N N F F F F F F < < < < < < $#F++ (  w||A.0IJJER7<< 24QRRTYZ   w||A$68OPP  ! ! C C$J    2/S /T%S4Z*@%A / / / /dQS QU[ Q Q Q Qhc cDj    6O0 O0 O0 O0 O0 O0f /33 3333 3 3 3lf
f f 	f
 :f d
f f f 	f f f fR0o 0# 0 0 0 0  QU      rM   