+
     h@  c                   s6   ^ RI t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
HtHtHtHt ^ RIHt ^ RIt^ RIt^ RIHtHt ^ RIHt ^RIHtHt ^RIHtHtHt ]P:                  ! R4      tR	tR
RRRRRRRRRR^<R^/t . RTOt!R t" ! R R]PF                  PH                  4      t% ! R R4      t&R R lt']&! ^(RRR7      t(]&! ^^^R7      t)]&! ^^<4      t*]&! ^^4      t+R R  lt,R! R" lt-R# R$ lt.]'! ]+4      RUR% R& ll4       t/]'! ])](4      R' R( l4       t0R) R* lt1R+/ R,R-/R. R/ llt2R0 R1 lt3R2 R3 lt4R4 R5 lt5R6 R7 lt6R8 R9 lt7R: R; lt8RVR< R= llt9 ! R> R?4      t:]! R@4      t;RA RB lt<RC RD lt=RE t>RF RG lt?RHRIRJRK/t@RL RM ltARNRORPRQ/tBRR RS ltCR# )W    N)OptionalSequenceTupleTypeVar)urljoin)BeautifulSoupTag)utils)MediaCategoryThumbnailType)AwaitingAuthorisationModdbExceptionRatelimitedmoddbzhttps://www.moddb.comZyeari Zmonthi $ Zweeki:	 ZdayiQ Zhouri  ZminuteZecondc                s  aa . oVV3R loS! V 4       V P                   P                  4       pRV9  d#   VP                  R4       VP                  R4       VRVP                  R4      ^,            pTP	                  S Uu. uF  q3P                  4       '       g   K  VNK  	  up4       RP                  V4      V n         V # u upi )z$Does it look like I'm enjoying this?c                 s  < V P                   R 8X  d   R# V P                  P                  4       pRV9   d"   WP                  R4      ^,           R S,           o\        P
                  ! V P                  4      pW"P                  R4      R pRV9   d   S! V P                  4       R# RV9   d   S! V P                  P                  4       R# R# )ZobjectN    Attributesz):zsuper().__init____init__)__name____doc__
splitlinesindexinspectZ	getsourcer   Z__base__)ZparentZdocsZsource
attributesget_docss   &  0/usr/lib/python3.14/site-packages/moddb/utils.pyr   Zconcat_docs.<locals>.get_docs3   s    ??h&~~((*t#jj)9:Q>@AJNJ""6??3T*,-'V__%6!V__--. "    r   z    -----------NZ
)r   r   Zappendr   Zextendstripjoin)ZclsZoriginalZfinalZxr   r   s   &   @@r   concat_docsr   /   s    J/" SM{{%%'Hx'())*;x~~&67!;<E	LLZ5Z779!!Z56))E"CKJ 6s   CCc                   s2   a a ] tR t^Qt oV 3R ltRtVtV ;t# )
SSLAdapterc                s   < \         P                  ! 4       p\         P                  P                  Vn        W2R &   \
        SV `  ! V/ VB # )ssl_context)sslZcreate_default_contextZ
TLSVersionZTLSv1_2Zmaximum_versionZsuperinit_poolmanager)selfargskwargsr   	__class__s   &*, r   r    ZSSLAdapter.init_poolmanagerR   s@    002&)nn&<&<# +}w'888r    )r   
__module____qualname____firstlineno__r    __static_attributes____classdictcell__Z__classcell__)r$   __classdict__s   @@r   r   r   Q   s     9 9r   r   c                   sR   a  ] tR t^Zt o RV 3R lR lltRV 3R lR lltR tRtV tR# )		RatelimitNc                s:   < V ^8  d   QhRS[ RS[ RS[R,          /# )   ratepersleepN)floatr   formatr+      "r   __annotate__Ratelimit.__annotate__[   s&      U  x~ r   c                s    Wn         W n        W0n        \        P                  P                  V n        \        P                  P                  V n        ^ V n        R# )r    N)r.   r/   r0   datetimeZminlast_calledinitial_call
call_count)r!   r.   r/   r0   s   &&&&r   r   ZRatelimit.__init__[   s@    	
#,,00$--11r   c                s4   < V ^8  d   QhRS[ P                   /# )r-   now)r7   r2   r4   r   r5   r6   d   s      ** r   c                sd    Vf   \         P                   P                  4       pWn        ^ V n        R # N)r7   r;   r9   r:   )r!   r;      &&r   resetZRatelimit.resetd   s(    ;##'')Cr   c                sv   \         P                   P                  4       pV P                  \         P                  ! V P                  R 7      ,           pW8  d'   \
        P                  R4       V P                  V4       V P                  ^,           V P                  8  d   W!,
          P                  4       pV P                  eO   W0P                  8:  d?   \
        P                  RV4       \        P                  ! V4       V P                  V4       M\        RV 2V4      hV ;P                  ^,          un        R# ))ZsecondszResetting ratelimitNzRatelimited! Sleeping for %sz Ratelimited please try again in )r7   r;   r9   Z	timedeltar/   LOGGERinfor>   r:   r.   Ztotal_secondsr0   timer   )r!   r;   ZexpiryZ	remaining   &   r   callZRatelimit.callk   s    ##%""X%7%7%II<KK-.JJsO??Q*446Izz%)zz*A:IF

9%

3!$DYK"PR[\\1r   )r:   r9   r8   r/   r.   r0   r<   )	r   r&   r'   r(   r   r>   rC   r)   r*   r+      @r   r,   r,   Z   s#        r   r,   c                $    V ^8  d   QhR\         /# )r-   limiters)r,   r3      "r   r5   r5      s       r   c                  s   a  V 3R  lpV# )c                 sJ   <a  \         P                  ! S 4      V V3R  l4       pV# )c                  sF   < S F  pVP                  4        K  	  S! V / VB # r<   )rC   )r"   r#   ZlimiterfuncrG   s   *, r   wrapperZ-ratelimit.<locals>.decorator.<locals>.wrapper   s(    # $ (((r   )	functoolsZwraps)rJ   rK   rG   s   f r   	decoratorZratelimit.<locals>.decorator   s%    			) 
	) r   r%   )rG   rM   s   j r   	ratelimitrN      s     r   i,  )r0   c                sD    V ^8  d   QhR\         R\        P                  /# )r-   dreturn)strr7   rH   rI   r   r5   r5      s      2 2 2)) 2r   c                s$    \         P                   P                  V RR V RR ,           R4      #   \         d     Mi ; i \         P                   P                  T R4      #   \         d     Mi ; i\         P                   P                  T R4      # )ag  A helper function that takes a ModDB string representation of time and returns an equivalent
datetime.datetime object. This can range from a datetime with the full year to
second to just a year and a month.

Parameters
-----------
d : str
    String representation of a datetime

Returns
-------
datetime.datetime
    The datetime object for the given string
Nz%Y-%m-%dT%H:%M:%S%zz%Y-%m-%dz%Y-%mi)r7   Zstrptime
ValueError)rO      &r   get_daterU      s      ))!CR&1RS6/;PQQ   ))!Z88  %%a11s   ,/ ==A! !A/.A/c                sX    V ^8  d   QhR\         P                  R\         P                  /# )r-   reqsession)requestsRequestSessionrH   rI   r   r5   r5      s&     ( ()) (H4D4D (r   c                s(   \         P                  ! VP                  4      pV P                  e   / V P                  CVCV n        MW n        RV P                  9  d(   \        P
                  ! \        4      V P                  R&   VP                  V 4      # )z/Prepared a request with the appropriate cookiesz
User-Agent)r   Zdict_from_cookiejarcookiesZheadersrandomZchoiceuser_agent_listprepare_request)rV   rW   r[      && r   r^   r^      sn    ''8G
{{0003;;&$*MM/$BL!""3''r   c                8    V ^8  d   QhR\         P                  /# )r-   response)rX   ZResponserH   rI   r   r5   r5      s     
 
x00 
r   c                s    V P                  4       pVP                  RR4      '       d   \        P                  VR,          4       \        P                  V P                  P
                  4       \        P                  V P                  P                  4       \        VR,          4      h V P                  4        RV P                  P                  4       9   d   \        R4      hR#   \        P                  P                   d     L]i ; i)z'Raise any error that could have occurederrorFtextzLis currently awaiting authorisation, which can take a couple of days while azEThis page is still await authorisation and cannot currently be parsedN)jsongetr?   rb   requesturlZbodyr   rX   Z
exceptionsZJSONDecodeErrorraise_for_statusrc   lowerr   )ra   rc   s   & r   rh   rh      s    }}88GU##LLf&LL))--.LL))../ f..	 $  	W== 	! $S
 	
	! .. s   'C' B C' 'D	D	c                sP    V ^8  d   QhR\         R\         R\        P                  /# )r-   usernamepasswordrW   )rQ   rX   rZ   rH   rI   r   r5   r5      s'      S C (BRBR r   c                sz   Vf"   \         P                  R,          P                  p\        P                  ! R\
         R24      pVP                  \        W24      4      p\        VP                  4      pVP                  RRR/R7      pVP                  RR	R
7      pVP                  RRRR7      pRRVR,          V VR,          RRVRR.RR/p	\        P                  ! R\
         R2WP                  R7      pVP                  \        W24      RR7      p
RV
P                  9  d   \        RV  24      hV
P                  # )zGLog a user in and return the `freeman` cookie containing the login hashr   GETz/members/loginformZnameZmembersform)attrsZinputZmembersusername)idrc   F)Ztypero   Zreferer rk   Z
remembermeZ1memberszSign inZPOST)datar[   )Zallow_redirectsZfreemanzLogin failed for user )sysmodulesSESSIONrX   rY   BASE_URLsendr^   souprc   findr[   rS   )rj   rk   rW   rV   resphtmlrm   Zusername_inputZ
botcatcherrr   Zlogins   &&&        r   generate_login_cookiesr|      s*    ++g&..


5XJn"=
>C<<56D		?D99VFM#:9;DYYw+<Y=N7E:J 	2v6BHse9D 

6hZ~#>TS_S_
`CLL6LNE%1(<====r   c                r`   )r-   rV   )rX   rY   rH   rI   r   r5   r5      s      !! r   c                s    \         P                  R,          P                  p\        W4      pVP	                  V4      p\        V4       V# )zHelper function to make get/post requests with the current SESSION object.

Parameters
-----------
req : requests.Request
    The request to perform

Returns
-------
requests.Response
    The returned response object

r   )rs   rt   ru   r^   rw   rh   )rV   rW   Zpreppedrz   rB   r   rf   rf      s=     !$G 4 < <Gc+G<< DTKr   c                0    V ^8  d   QhR\         R\        /# )r-   r{   rP   )rQ   r   rH   rI   r   r5   r5     s     . .s .} .r   c                s    \        V R4      # )zSimple helper function that takes a string representation of an html page and
returns a beautiful soup object

Parameters
-----------
html : str
    The string representationg of the html to parse

Returns
--------
bs4.BeautifulSoup
    The parsed html
zhtml.parser)r   )r{   rT   r   rx   rx     s     }--r   paramsrd   Fc                s<    V ^8  d   QhR\         R\        R\        /# )r-   rg   r~   rd   )rQ   ZdictZboolrH   rI   r   r5   r5     s!      # $ 4 r   c               s    \        \        P                  ! RWR7      4      pV'       d   VP                  4       # \	        VP
                  4      # )a  A helper function that takes a url and returns a beautiful soup objects. This is used to center
the request making section of the library. Can also be passed a set of paramaters, used for sorting
and filtering in the search function.

Parameters
-----------
url : str
    The url to get
params : dict
    A dictionnary of filters and sorting key-value pairs.
json : Optional[bool]
    Whether the expected response is json, in which case it will not be soup'd

Returns
-------
bs4.BeautifulSoup
    The parsed html
rl   )r~   )rf   rX   rY   rd   rx   rc   )rg   r~   rd   rz   s   &$$ r   get_pager     s8    & 8##E3>?Dyy{		?r   c                sR    V ^8  d   QhR\         R\        \        \        3,          /# r-   stringrP   )rQ   r   intrH   rI   r   r5   r5   8  s"      c eCHo r   c                s    \         P                  ! RV 4      p\        VP                  ^4      P	                  RR4      4      p\        VP                  ^4      P	                  RR4      4      pW#3# )al  A helper function that takes a string representation of total something and
daily amount of that same thing and returns both as a tuple of ints.

Parameters
------------
string : str
    The string containing the numbers both total and daily

Returns
--------
Tuple[int, int]
    Tuple contains the total views (first element) and the daily views (second element)
z^([0-9,]*) \(([0-9,]*) today\)$,rp   )reZsearchr   groupreplace)r   matchesZviewsZtodayrB   r   	get_viewsr   8  s]     ii:FCGa ((b12Ea ((b12E<r   c                0    V ^8  d   QhR\         R\         /# )r-   pathrP   rQ   rH   rI   r   r5   r5   M  s      s s r   c                s\    V P                  \        4      '       g   \        \        V 4      # V # )zJoins a partial moddb url with the base url and returns the combined url

Parameters
-----------
path : str
    the url to join

Return
-------
str
    The full url.

)Z
startswithrv   r   )r   rT   r   r   r   M  s%     ??8$$x&&Kr   c                r   r   r   rH   rI   r   r5   r5   a  s     X Xc Xc Xr   c                s    V P                  RR4      P                  RR4      P                  RR4      P                  4       # )a  Removes all extra fluff from a stat to get the barebone content.

Stats usually have extra words like "members" or "visitors" and have command separated integers.

Parameters
-----------
string : str
    The string to clean up

Returns
--------
str
    The cleaned up stat
r   rp   rq   Zmember)r   r   )r   rT   r   	normalizer   a  s8     >>#r"**9b9AA(BOUUWWr   c                r}   )r-   imgrP   )r   r	   rH   rI   r   r5   r5   s  s     # # # #r   c                s    V f   \         P                  # V R,          RR R8X  d   \         P                  # \         P                  # )zDetermines whether a media is an image, a video or an audio.

This is somewhat of a brittle method, don't rely on it too much.

Parameters
-----------
img: bsa.Tag
    The image to check

Returns
---------
MediaCategory
    The category of the media
Zsrcz.mp4ii)r	   audiovideoimage)r   rT   r   get_media_typer   s  s@     {"""	UBr	f	$""""""r   c                s(    V ^8  d   QhR\         RR/# )r-   rg   rP   r
   r   rH   rI   r   r5   r5     s     ! !s ! !r   c                sL   RpRRRRRRRRRR	R
RRR	/p\         P                  ! W4      pV^ ,          P                  R4      '       d   VR,          ^ R MV^ ,          p \        V,          p\
        P                  RW4       V#   \         d    \        Y$,          ,          p L8i ; i)zGet the page type based on a url.

Parameters
-----------
url : str
    The url to get

Return
-------
ThumbnailType
    The type of the page
z\/((?!page|pages\b)\b\w+)\/ZnewZarticleZfeatureZtutorialZdownloadZfiler   Zmediar   z mediar   Zsz%s is type %s)r   ZfindallZendswithr
   ZKeyErrorr?   r@   )rg   ZregexZtype_mappingr   matchZ	page_types   &     r   get_page_typer     s     +Ey9IFL jj$G!(!4!4S!9!9GBK"wqzE7!%(	 KK0	  7!,"56	7s   B B#"B#c                s<    V ^8  d   QhR\         R\         R\         /# )r-   abrP   )r   rH   rI   r   r5   r5     s!      s s s r   c                s    W) ,          ) # )z+Like a // b but rounded up instead of down.r%   )r   r   r=   r   ceildivr     s    "W:r   c          	      s|    V ^8  d   QhR\         P                  R\        R\        \        \        \        3,          /# )r-   
result_boxper_pagerP   )bs4r   r   r   rH   rI   r   r5   r5     s4     "/ "/s00 "/C "/sTWY\}I] "/r   c                s   \         P                  ! RV P                  RRR7      P                  RRR7      P                  RRR7      P                  4      pV'       g   R
# \	        VP                  ^4      P                  RR	4      4      p\	        VP                  ^4      P                  RR	4      4      p\        WA4      p\        W14      pWeV3# )a  Get the current page, total pages and total results from
a result list

Parameters
------------
result_box: bs4.BeautifulSoup
    The HTML of the result box from a list of results page
per_page: Optional[int]
    The number of results per page, important for calculations. Defaults
    to 30, doesn't usually need to be touched

Returns
--------
Tuple[int, int, int]
    The stats in order of: number of current page (starting from 1),
    total number of pages (between 1 and X) and the total results.
z(.*\(([0-9,]*) - ([0-9,]*) of ([0-9,]*)\)ZdivZnormalcorner)Zclass_ZtitleZspanZheadingr   rp   )   r   N)r   r   ry   r   r   r   r   r   )r   r   ZstatsZmax_resultsZall_resultsZmax_pageZcurrent_pages   &&     r   get_list_statsr     s    $ HH3n5	eG	$	fY	'	E ekk!n,,S"56Kekk!n,,S"56K{-H;1L;..r   c                   s*   a  ] tR tRt o RtR tRtV tR# )Objecti  zEA dud objects that will transform every kwarg given into an attributec                s<    V P                   P                  V4       R # r<   )Z__dict__Zupdate)r!   r#   s   &,r   r   ZObject.__init__  s    V$r   r%   N)r   r&   r'   r(   r   r   r)   r*   rD   rE   r   r   r     s     O% %r   r   Dc                \    V ^8  d   QhR\         \        ,          R\        \        ,          /# )r-   seqrP   r   r   r   rH   rI   r   r5   r5     s"      ! ! r   c                s<    V F  pV ! V4      '       g   K  Vu # 	  R# )a}  A helper to return the first element found in the sequence
that meets the predicate. For example: ::

    comment = find(lambda comment: comment.author.name == 'SilverElf', mod.comments.flatten())

would find the first :class:`.Comment` whose author's name is 'SilverElf' and return it.
If no entry is found, then ``None`` is returned.

This is different from `filter`_ due to the fact it stops the moment it finds
a valid entry.

.. _filter: https://docs.python.org/3.6/library/functions.html#filter

Parameters
-----------
predicate
    A function that returns a boolean-like result.
seq : iterable
    The iterable to search through.
Nr%   )	predicater   Zelementr_   r   ry   ry     s#    , WN  r   c                r   )r-   iterablerP   r   rH   rI   r   r5   r5     s"     7% 7%(1+ 7%8A; 7%r   c                s&   a V3R lp\        W 4      # )a>  A helper that returns the first element in the iterable that meets
all the traits passed in ``attrs``. This is an alternative for
:func:`moddb.utils.find`.

When multiple attributes are specified, they are checked using
logical AND, not logical OR. Meaning they have to meet every
attribute passed in and not one of them.

To have a nested attribute search (i.e. search by ``x.y``) then
pass in ``x__y`` as the keyword argument.

If nothing is found that matches the attributes passed, then
``None`` is returned.

Examples
---------

Basic usage:

.. code-block:: python3

    article = moddb.utils.get(mod.get_articles(), name='Version 3.5 Released')

Multiple attribute matching:

.. code-block:: python3

    comment = moddb.utils.get(mod.get_comments(2), content='Test', karma=3)

Nested attribute matching:

.. code-block:: python3

    comment = moddb.utils.get(article.get_comments(), author__name='SilverElf', content='Best article ever')

Parameters
-----------
iterable
    An iterable to search through.
\*\*attrs
    Keyword arguments that denote attributes to search with.
c                 s   < SP                  4        F5  w  rVP                  R 4      pT pV F  p\        WE4      pK  	  WB8w  g   K4   R# 	  R# )Z__FT)ZitemssplitZgetattr)ZelemZattrZvalZnestedZobjZ	attributern   s   &     r   r   Zget.<locals>.predicate*  sK    IDZZ%FC#	c- $ z ' r   )ry   )r   rn   r   s   &l r   re   re     s    X	 	$$r   c                  s@    \         P                  ! 4       P                  # r<   )uuidZuuid4Zhexr%   r   r   generate_hashr   8  s    ::<r   c                r   )r-   rg   rP   r   rH   rI   r   r5   r5   <  s      c c r   c                s2    V P                  R4      R,          # )zGet the site area from a urlZ/rR   )r   )rg   rT   r   get_sitearear   <  s    99S>"r   Z3ZmodsZ2Zgamesc                rF   )r-   keyr   rH   rI   r   r5   r5   G  s     4 4 4r   c                s@    \         P                  \        V 4      R4      # )zGet the sitearea id from an intZnone)siteareaid_mappingre   rQ   )r   rT   r   get_siteareaidr   G  s    !!#c(F33r   Zki  Zmi@B c                r}   r   )rQ   r   rH   rI   r   r5   r5   O  s      # # r   c                 s    V R,          P                  4       pVP                  4       '       d   \        V 4      # \        V RR 4      \        V,          ,          p\        V4      # )r   Nr   )ri   Zisdigitr   r1   number_mapping)r   Z
identifierZnumbers   &  r   unroll_numberr   O  sQ    !!#J6{6#2;."<<Fv;r   )zoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36zMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.2792.65zMozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edge/44.18363.8131zPMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0zPMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0z}Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 OPR/114.0.0.0z}Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 OPR/114.0.0.0r<   )i   )Dr7   rL   r   Zloggingr\   r   r   rs   rA   r   Ztypingr   r   r   r   Zurllib.parser   r   rX   r   r   r   Zenumsr	   r
   Zerrorsr   r   r   Z	getLoggerr?   rv   Ztime_mappingr]   r   ZadaptersZHTTPAdapterr   r,   rN   ZGLOBAL_LIMITERZGLOBAL_THROTLEZCOMMENT_LIMITERZLOGIN_LIMITERrU   r^   rh   r|   rf   rx   r   r   r   r   r   r   r   r   r   r   ry   re   r   r   r   r   r   r   r%   r   r   <module>r      s        	 
 
   5 5   
  "  / F F			7	#" IW
F	5
DbQD9"".. 9" "J 2s#.1aq)Ar"!Q28(
. = > >>* +,.$ % 4*(X$#.!H
"/J% % CL87%t  4
 uc9-r   