§
    š¿j¹2  ã                  ól   — d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	m
Z
 ddlmZ  G d„ d¦  «        ZdS )z£
FreeExile Social Repository: SQLite Persistence Layer for Friends & Social Engine.
Enforces normalized player friendships, bidirectional blocklist, and indexing.
é    )ÚannotationsN)ÚAnyÚDictÚListÚOptional)ÚFriendshipStatusc                  ó¤   — e Zd ZdZd"d#d„Zd$d„Zd%d
„Zd$d„Zd&d„Zd&d„Z	d&d„Z
d&d„Zd&d„Zd&d„Zd'd„Zd'd„Zd(d„Zd)d„Zd*d„Zd+d„Zd+d„Zd+d „Zd!S ),ÚSocialRepositoryzQSQLite repository managing player friendships, pending requests, and block lists.údata/social_store.dbÚdb_pathÚstrÚreturnÚNonec                ór   — || _         d | _        |                      ¦   «          |                      ¦   «          d S )N)Ú_db_pathÚ_persistent_connÚ_ensure_storage_dirÚ_init_db)Úselfr   s     ú8C:\Projects\FreeExile\server\social\social_repository.pyÚ__init__zSocialRepository.__init__   s4   € ØˆŒØ>BˆÔØ× Ò Ñ"Ô"Ð"Ø�Š‰Œˆˆˆó    c                óœ   — | j         dk    r>t          j                             | j         ¦  «        }|rt          j        |d¬¦  «         dS dS dS )z>Creates target directory for SQLite database if not in-memory.ú:memory:T)Úexist_okN)r   ÚosÚpathÚdirnameÚmakedirs)r   r   s     r   r   z$SocialRepository._ensure_storage_dir   sY   € àŒ=˜JÒ&Ð&Ý”g—o’o d¤mÑ4Ô4ˆGØð 4Ý”˜G¨dÐ3Ñ3Ô3Ð3Ð3Ð3ð 'Ð&ð4ð 4r   úsqlite3.Connectionc                ór  — | j         dk    rW| j        €It          j        d¦  «        | _        t          j        | j        _        | j                             d¦  «         | j        S t          j        | j         ¦  «        }t          j        |_        |                     d¦  «         |                     d¦  «         |S )z@Returns SQLite connection with row factory and WAL journal mode.r   NzPRAGMA foreign_keys = ON;zPRAGMA journal_mode = WAL;)r   r   Úsqlite3ÚconnectÚRowÚrow_factoryÚexecute©r   Úconns     r   Ú_get_connectionz SocialRepository._get_connection   sž   € àŒ=˜JÒ&Ð&ØÔ$Ð,Ý(/¬¸
Ñ(CÔ(C�Ô%Ý4;´K�Ô%Ô1ØÔ%×-Ò-Ð.IÑJÔJÐJØÔ(Ð(åŒ˜tœ}Ñ-Ô-ˆÝ"œ;ˆÔØ�ŠÐ0Ñ1Ô1Ð1Ø�ŠÐ1Ñ2Ô2Ð2Øˆr   c                ó€   — |                       ¦   «         }|                     d¦  «         |                     ¦   «          dS )z.Initializes tables and indices if not present.ac  
            CREATE TABLE IF NOT EXISTS player_friendships (
                user_id TEXT NOT NULL,
                friend_id TEXT NOT NULL,
                status TEXT NOT NULL,
                friend_note TEXT NOT NULL DEFAULT '',
                created_at REAL NOT NULL,
                updated_at REAL NOT NULL,
                PRIMARY KEY (user_id, friend_id)
            );
            CREATE INDEX IF NOT EXISTS idx_friendships_user_status ON player_friendships(user_id, status);
            CREATE INDEX IF NOT EXISTS idx_friendships_friend_status ON player_friendships(friend_id, status);
            N)r)   ÚexecutescriptÚcommitr'   s     r   r   zSocialRepository._init_db-   sC   € à×#Ò#Ñ%Ô%ˆØ×Òðñ	
ô 	
ð 	
ð 	�Š‰Œˆˆˆr   Úuser_idÚ	friend_idÚboolc           	     óþ   — |r|sdS |                       ¦   «         }|                     ¦   «         }|                     d||t          j        j        ||t          j        j        f¦  «         |                     ¦   «         duS )z?Checks mutual block: True if either user has blocked the other.Fz�SELECT 1 FROM player_friendships WHERE (user_id = ? AND friend_id = ? AND status = ?) OR (user_id = ? AND friend_id = ? AND status = ?) LIMIT 1N)r)   Úcursorr&   r   ÚBLOCKEDÚvalueÚfetchone©r   r-   r.   r(   Úcurs        r   Ú
is_blockedzSocialRepository.is_blockedA   sƒ   € àð 	˜ið 	Ø�5Ø×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠðHà�iÕ!1Ô!9Ô!?ÀÈGÕUeÔUmÔUsÐtñ	
ô 	
ð 	
ð
 �|Š|‰~Œ~ TÐ)Ð)r   c                ó  — |r|r||k    s|                       ||¦  «        rdS |                      ¦   «         }|                     ¦   «         }|                     d||t          j        j        f¦  «         |                     ¦   «         duS )zOReturns True if both users mutually accepted friendship and neither is blocked.Fz[SELECT 1 FROM player_friendships WHERE user_id = ? AND friend_id = ? AND status = ? LIMIT 1N)r7   r)   r1   r&   r   ÚACCEPTEDr3   r4   r5   s        r   Ú	is_friendzSocialRepository.is_friendN   s’   € àð 	˜ið 	¨7°iÒ+?Ð+?À4Ç?Â?ÐSZÐ\eÑCfÔCfÐ+?Ø�5Ø×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØiØ�iÕ!1Ô!:Ô!@ÐAñ	
ô 	
ð 	
ð �|Š|‰~Œ~ TÐ)Ð)r   c           
     ó|  — |r|r||k    rdS |                       ||¦  «        s|                      ||¦  «        rdS t          j        ¦   «         }|                      ¦   «         }|                     ¦   «         }|                     d||f¦  «         |                     ¦   «         }|r,|d         t          j        j	        t          j
        j	        fv rdS |                     d||t          j        j	        ||||f¦  «         |                     d||t          j        j	        ||||f¦  «         |                     ¦   «          dS )z;Sends an outgoing friend request from user_id to friend_id.FúISELECT status FROM player_friendships WHERE user_id = ? AND friend_id = ?ÚstatuszâINSERT OR REPLACE INTO player_friendships (user_id, friend_id, status, friend_note, created_at, updated_at) VALUES (?, ?, ?, COALESCE((SELECT friend_note FROM player_friendships WHERE user_id = ? AND friend_id = ?), ''), ?, ?)T)r7   r:   Útimer)   r1   r&   r4   r   ÚPENDING_SENTr3   r9   ÚPENDING_RECEIVEDr,   )r   r-   r.   Únowr(   r6   Úexistings          r   Úadd_friend_requestz#SocialRepository.add_friend_requestZ   sV  € àð 	˜ið 	¨7°iÒ+?Ð+?Ø�5Ø�?Š?˜7 IÑ.Ô.ð 	°$·.².ÀÈ)Ñ2TÔ2Tð 	Ø�5åŒi‰kŒkˆØ×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠÐ_ÐbiÐktÐauÑvÔvÐvØ—<’<‘>”>ˆØð 	˜ Ô*Õ/?Ô/LÔ/RÕTdÔTmÔTsÐ.tÐtÐtØ�5à�ŠðEà�iÕ!1Ô!>Ô!DÀgÈyÐZ]Ð_bÐcñ	
ô 	
ð 	
ð
 	�ŠðEà˜Õ!1Ô!BÔ!HÈ)ÐU\Ð^aÐcfÐgñ	
ô 	
ð 	
ð
 	�Š‰ŒˆØˆtr   c                ó  — |r|r|                       ||¦  «        rdS t          j        ¦   «         }|                      ¦   «         }|                     ¦   «         }|                     d||f¦  «         |                     ¦   «         }|r|d         t          j        j        k    rdS |                     dt          j	        j        |||f¦  «         |                     dt          j	        j        |||f¦  «         | 
                    ¦   «          dS )z>Accepts a pending incoming request for user_id from friend_id.Fr<   r=   z\UPDATE player_friendships SET status = ?, updated_at = ? WHERE user_id = ? AND friend_id = ?T)r7   r>   r)   r1   r&   r4   r   r@   r3   r9   r,   )r   r-   r.   rA   r(   r6   Úrows          r   Úaccept_friend_requestz&SocialRepository.accept_friend_requestv   s  € àð 	˜ið 	¨4¯?ª?¸7ÀIÑ+NÔ+Nð 	Ø�5ÝŒi‰kŒkˆØ×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠÐ_ÐbiÐktÐauÑvÔvÐvØ�lŠl‰nŒnˆØð 	�c˜(”mÕ'7Ô'HÔ'NÒNÐNØ�5à�ŠØjÝÔ&Ô,¨c°7¸IÐFñ	
ô 	
ð 	
ð 	�ŠØjÝÔ&Ô,¨c°9¸gÐFñ	
ô 	
ð 	
ð 	�Š‰ŒˆØˆtr   c                óH  — |                       ¦   «         }|                     ¦   «         }|                     d||f¦  «         |                     ¦   «         }|r|d         t          j        j        k    rdS |                     d||||f¦  «         |                     ¦   «          dS )z3Declines an incoming friend request from friend_id.r<   r=   FúgDELETE FROM player_friendships WHERE (user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)T)r)   r1   r&   r4   r   r@   r3   r,   ©r   r-   r.   r(   r6   rE   s         r   Údecline_friend_requestz'SocialRepository.decline_friend_request�   s¤   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠÐ_ÐbiÐktÐauÑvÔvÐvØ�lŠl‰nŒnˆØð 	�c˜(”mÕ'7Ô'HÔ'NÒNÐNØ�5à�ŠØuØ�i ¨GÐ4ñ	
ô 	
ð 	
ð 	�Š‰ŒˆØˆtr   c                óâ   — |                       ||¦  «        sdS |                      ¦   «         }|                     ¦   «         }|                     d||||f¦  «         |                     ¦   «          dS )zFRemoves an accepted friend relationship between user_id and friend_id.FrH   T)r:   r)   r1   r&   r,   r5   s        r   Úremove_friendzSocialRepository.remove_friend�   st   € à�~Š~˜g yÑ1Ô1ð 	Ø�5Ø×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØuØ�i ¨GÐ4ñ	
ô 	
ð 	
ð 	�Š‰ŒˆØˆtr   Ú	target_idc                óÒ  — |r|r||k    rdS t          j         ¦   «         }|                      ¦   «         }|                     ¦   «         }|                     d||f¦  «         |                     ¦   «         }|r3|d         t
          j        j        k    r|                     d||f¦  «         |                     d||t
          j        j        ||f¦  «         |                     ¦   «          dS )zLBlocks target_id by user_id and wipes any existing non-blocked relationship.Fr<   r=   zBDELETE FROM player_friendships WHERE user_id = ? AND friend_id = ?z†INSERT OR REPLACE INTO player_friendships (user_id, friend_id, status, friend_note, created_at, updated_at) VALUES (?, ?, ?, '', ?, ?)T)	r>   r)   r1   r&   r4   r   r2   r3   r,   )r   r-   rM   rA   r(   r6   Ú
target_rels          r   Úblock_playerzSocialRepository.block_playerª   sñ   € àð 	˜ið 	¨7°iÒ+?Ð+?Ø�5ÝŒi‰kŒkˆØ×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠÐ_ÐbkÐmtÐauÑvÔvÐvØ—\’\‘^”^ˆ
Øð 	t˜* XÔ.Õ2BÔ2JÔ2PÒPÐPØ�KŠKÐ\Ð_hÐjqÐ^rÑsÔsÐsà�Šð)à�iÕ!1Ô!9Ô!?ÀÀcÐJñ	
ô 	
ð 	
ð
 	�Š‰ŒˆØˆtr   c                óJ  — |                       ¦   «         }|                     ¦   «         }|                     d||t          j        j        f¦  «         |                     ¦   «         sdS |                     d||t          j        j        f¦  «         |                     ¦   «          dS )z,Removes target_id from user_id's block list.zSSELECT 1 FROM player_friendships WHERE user_id = ? AND friend_id = ? AND status = ?FzQDELETE FROM player_friendships WHERE user_id = ? AND friend_id = ? AND status = ?T)r)   r1   r&   r   r2   r3   r4   r,   )r   r-   rM   r(   r6   s        r   Úunblock_playerzSocialRepository.unblock_player¾   sŸ   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØaØ�iÕ!1Ô!9Ô!?Ð@ñ	
ô 	
ð 	
ð �|Š|‰~Œ~ð 	Ø�5Ø�ŠØ_Ø�iÕ!1Ô!9Ô!?Ð@ñ	
ô 	
ð 	
ð 	�Š‰ŒˆØˆtr   Únotec                óR  — |                       ||¦  «        sdS |pddd…         }t          j        ¦   «         }|                      ¦   «         }|                     ¦   «         }|                     d||||t
          j        j        f¦  «         |                     ¦   «          |j	        dk    S )zASets a personal note for an accepted friend (capped at 64 chars).FÚ Né@   zpUPDATE player_friendships SET friend_note = ?, updated_at = ? WHERE user_id = ? AND friend_id = ? AND status = ?r   )
r:   r>   r)   r1   r&   r   r9   r3   r,   Úrowcount)r   r-   r.   rS   Ú
clean_noterA   r(   r6   s           r   Úset_friend_notez SocialRepository.set_friend_noteÏ   s¢   € à�~Š~˜g yÑ1Ô1ð 	Ø�5Ø�j˜b # 2 #Ô&ˆ
ÝŒi‰kŒkˆØ×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØ~Ø˜˜g yÕ2BÔ2KÔ2QÐRñ	
ô 	
ð 	
ð 	�Š‰ŒˆØŒ|˜aÒÐr   c                ó  — |                       ¦   «         }|                     ¦   «         }|                     d||t          j        j        f¦  «         |                     ¦   «         }|r|d         rt          |d         ¦  «        ndS )z?Retrieves stored note for friend_id from user_id's perspective.z]SELECT friend_note FROM player_friendships WHERE user_id = ? AND friend_id = ? AND status = ?Úfriend_noterU   )r)   r1   r&   r   r9   r3   r4   r   rI   s         r   Úget_friend_notez SocialRepository.get_friend_noteÞ   s€   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØkØ�iÕ!1Ô!:Ô!@ÐAñ	
ô 	
ð 	
ð �lŠl‰nŒnˆØ*-ÐL°#°mÔ2DÐL�s�3�}Ô%Ñ&Ô&Ð&È"ÐLr   úList[Dict[str, Any]]c           
     óÖ  — |                       ¦   «         }|                     ¦   «         }|                     d|t          j        j        f¦  «         |                     ¦   «         }g }|D ]€}t          |d         ¦  «        }|                      ||¦  «        sS| 	                    |t          |d         ¦  «        t          |d         ¦  «        t          |d         ¦  «        dœ¦  «         Œ�|S )zDReturns all accepted friends with metadata, excluding blocked users.z†SELECT friend_id, friend_note, created_at, updated_at FROM player_friendships WHERE user_id = ? AND status = ? ORDER BY created_at ASCr.   r[   Ú
created_atÚ
updated_at)r.   r[   r_   r`   )r)   r1   r&   r   r9   r3   Úfetchallr   r7   ÚappendÚfloat)r   r-   r(   r6   ÚrowsÚfriendsrE   Úfids           r   Úget_friendszSocialRepository.get_friendsé   sñ   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠðGàÕ&Ô/Ô5Ð6ñ	
ô 	
ð 	
ð
 �|Š|‰~Œ~ˆØ(*ˆØð 	ð 	ˆCÝ�c˜+Ô&Ñ'Ô'ˆCØ—?’? 7¨CÑ0Ô0ð Ø—’Ø!$Ý#& s¨=Ô'9Ñ#:Ô#:Ý"'¨¨LÔ(9Ñ":Ô":Ý"'¨¨LÔ(9Ñ":Ô":ð	 ð  ñ ô ð øð ˆr   ú	List[str]c                óÜ   — |                       ¦   «         }|                     ¦   «         }|                     d|t          j        j        f¦  «         d„ |                     ¦   «         D ¦   «         S )z.Returns list of player IDs blocked by user_id.úaSELECT friend_id FROM player_friendships WHERE user_id = ? AND status = ? ORDER BY created_at ASCc                ó8   — g | ]}t          |d          ¦  «        ‘ŒS ©r.   ©r   ©Ú.0rE   s     r   ú
<listcomp>z5SocialRepository.get_blocked_list.<locals>.<listcomp>  ó%   € Ð@Ð@Ð@¨#•�C˜Ô$Ñ%Ô%Ð@Ð@Ð@r   )r)   r1   r&   r   r2   r3   ra   ©r   r-   r(   r6   s       r   Úget_blocked_listz!SocialRepository.get_blocked_listÿ   sg   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØoØÕ&Ô.Ô4Ð5ñ	
ô 	
ð 	
ð AÐ@°·²±´Ð@Ñ@Ô@Ð@r   c                óV  — |                       ¦   «         }|                     ¦   «         }|                     d|t          j        j        f¦  «         g }|                     ¦   «         D ]B}t          |d         ¦  «        }|                      ||¦  «        s| 	                    |¦  «         ŒC|S )z@Returns player IDs that sent pending friend requests to user_id.rj   r.   )
r)   r1   r&   r   r@   r3   ra   r   r7   rb   )r   r-   r(   r6   ÚresultsrE   Ú	sender_ids          r   Úget_pending_requestsz%SocialRepository.get_pending_requests	  s¦   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØoØÕ&Ô7Ô=Ð>ñ	
ô 	
ð 	
ð ˆØ—<’<‘>”>ð 	*ð 	*ˆCÝ˜C Ô,Ñ-Ô-ˆIØ—?’? 7¨IÑ6Ô6ð *Ø—’˜yÑ)Ô)Ð)øØˆr   c                óÜ   — |                       ¦   «         }|                     ¦   «         }|                     d|t          j        j        f¦  «         d„ |                     ¦   «         D ¦   «         S )z8Returns player IDs to whom user_id sent friend requests.rj   c                ó8   — g | ]}t          |d          ¦  «        ‘ŒS rl   rm   rn   s     r   rp   z6SocialRepository.get_sent_requests.<locals>.<listcomp>   rq   r   )r)   r1   r&   r   r?   r3   ra   rr   s       r   Úget_sent_requestsz"SocialRepository.get_sent_requests  sg   € à×#Ò#Ñ%Ô%ˆØ�kŠk‰mŒmˆØ�ŠØoØÕ&Ô3Ô9Ð:ñ	
ô 	
ð 	
ð AÐ@°·²±´Ð@Ñ@Ô@Ð@r   N)r   )r   r   r   r   )r   r   )r   r    )r-   r   r.   r   r   r/   )r-   r   rM   r   r   r/   )r-   r   r.   r   rS   r   r   r/   )r-   r   r.   r   r   r   )r-   r   r   r]   )r-   r   r   rh   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r)   r   r7   r:   rC   rF   rJ   rL   rP   rR   rY   r\   rg   rs   rw   rz   © r   r   r
   r
      s•  € € € € € Ø[Ð[ðð ð ð ð ð4ð 4ð 4ð 4ðð ð ð ðð ð ð ð(*ð *ð *ð *ð
*ð 
*ð 
*ð 
*ðð ð ð ð8ð ð ð ð.ð ð ð ð ð ð ð ðð ð ð ð(ð ð ð ð" ð  ð  ð  ð	Mð 	Mð 	Mð 	Mðð ð ð ð,Að Að Að Aðð ð ð ðAð Að Að Að Að Ar   r
   )r~   Ú
__future__r   r   r"   r>   Útypingr   r   r   r   Úserver.social.social_typesr   r
   r   r   r   ú<module>rƒ      sº   ððð ð
 #Ð "Ð "Ð "Ð "Ð "Ø 	€	€	€	Ø €€€Ø €€€Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø 7Ð 7Ð 7Ð 7Ð 7Ð 7ðRAð RAð RAð RAð RAñ RAô RAð RAð RAð RAr   