
    (<i]                     P   d 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  ed      Z e	e      ej                  vr"ej                  j                  d e	e             ddlmZ 	 ddlmZ ddlmZ  ed      Zedz  ZdZ ed      Zg dZg dZdddiddiddiddigiddddddddddd dd!d"dgigd#did$Zd%d&d'dZd(e	d)dfd*Zd+e	d,e	d)e	fd-Zd.e	dz  d)dfd/Z d.e	dz  d)dfd0Z!d1ed2e	d3e	d4e"d5e#d6e	d7e	d8e#d)e#fd9Z$d1ed:e	d;e	d3e	d<e	d)e#fd=Z%d>e#d)dfd?Z&d)ejN                  fd@Z(e)dAk(  rL e(       Z*e*jV                  r% e e*jX                  B        ej*                  d        e!e*jX                  B       yy# e$ r(Z ed	e         ej*                  d
       Y dZ[0dZ[ww xY w)CuQ  
Meta Marketing API - 리쿠르팅 광고 캠페인 셋업 스크립트

담당: 엔키 (개발5팀)
생성일: 2026-04-06

실행 예시:
    python meta_campaign_setup.py
    python meta_campaign_setup.py --page-id 1234567890
    python meta_campaign_setup.py --dry-run
    python meta_campaign_setup.py --page-id 1234567890 --dry-run
    N)datetime)Pathz/home/jay/workspace)MetaAdsClient)Ad)	AdAccountu:   [ERROR] facebook_business SDK를 찾을 수 없습니다:    z*/home/jay/workspace/teams/dev5-team/outputzmeta_campaign_result.jsonzhttps://incar-top1.tistory.comz+/home/jay/workspace/output/banners/versions)	)v-round2zcell-1-incar-fair   인카금융서비스)r	   zcell-2-incar-leaderr
   )r	   zcell-3-incar-supportr
   )v1460zcell-4-ga-fairGA)r   zcell-5-ga-leaderr   )r   zcell-6-ga-supportr   )r	   zcell-7-snu-fair   서울대보험쌤)r	   zcell-8-snu-leaderr   )r	   zcell-9-snu-supportr   )r
   r   r   regionskey2004200620012010   7   	interests6003217093576u   보험)idname6004037215009u   구직6003074954515u   영업6003115782742u   보험계약advantage_audience)geo_locationsage_minage_maxflexible_spectargeting_automationuz   인카금융서비스와 함께 성공적인 보험 커리어를 시작하세요! 경력직/신입 모두 환영합니다.um   GA(General Agency)에서 당신의 보험 전문가 역량을 키워보세요. 지금 바로 지원하세요!uq   서울대 출신 보험 전문가들과 함께하는 성장의 기회! 서울대보험쌤 팀에 합류하세요.msgreturnc                 l    t        j                         j                  d      }t        d| d|         y)u%   타임스탬프 포함 콘솔 로그.z%H:%M:%S[z] N)r   nowstrftimeprint)r#   tss     \/home/jay/workspace/.worktrees/task-2057-dev2/teams/dev5-team/scripts/meta_campaign_setup.pylogr,   h   s,    		 	 	,B	AbTC5/    versioncell_dirc                 2    t        t        | z  |z  dz        S )u/   배너 이미지 절대 경로를 반환한다.zmeta-feed-1080x1080.png)strBANNER_BASE)r.   r/   s     r+   
image_pathr3   n   s    {W$x/2KKLLr-   page_idc                    t         j                  j                  dd      }t        d       t        d       t        d       t        d       t        d       t        D ]C  \  }}}t        ||      }t        |      j                         rdnd	}t        d
| d| d|        E t        d       dddddd|dddddddddddg}|D ]   }t        d|d    d|d    d |d!    d"       " t        d#       |D ]1  }t        D ]&  }|d$    d%| }	t        d|	 d&|d'    d(|d)           ( 3 | r_t        d*|  d+       t        D ];  }t        D 
cg c]  \  }
}}||k(  s| }}}
}|D ]  }t        d,| d%|         = t        d-       nt        d.       t        d/       t        d0t                t        d1       yc c}}}
w )2u7   실제 API 호출 없이 실행 구조를 출력한다.META_PIXEL_ID1461062562329883z=
============================================================u-     DRY-RUN 모드 — 실제 API 호출 없음z<============================================================u=   
[1] MetaAdsClient 초기화 + 토큰/계정 유효성 확인u$   
[2] 배너 이미지 9장 업로드u   ✓u   ✗ 파일없음z    z  u     →  u#   
[3] 캠페인 2개 생성 (PAUSED)   리쿠르팅_리드_2026Q2OUTCOME_LEADS   리드i  LEAD_GENERATIONIMPRESSIONSLEADpixel_idcustom_event_typer   	objectivealiasdaily_budgetopt_goalbillingpromoted_object    리쿠르팅_잠재고객_2026Q2OUTCOME_TRAFFIC   잠재고객LINK_CLICKSNz    - r   z  objective=rB   z  daily_budget=rD      ¢u1   
[4] 광고세트 6개 생성 (PAUSED, 각 350¢)rC   _z  opt=rE   z
  billing=rF   u<   
[5] 크리에이티브 9개 + 광고 18개 생성 (page_id=)u       크리에이티브: uQ       → 각 광고세트(6개) × 3개 크리에이티브 = 18개 광고 (PAUSED)u=   
[5] 크리에이티브/광고 생성 스킵 (page_id 없음)u   
[6] 결과 JSON 저장u       → z>
============================================================
)
osenvirongetr)   CELL_MAPr3   r   existsADSET_BRANDSOUTPUT_FILE)r4   r?   r.   r/   brandprS   campaigns_infocr   rM   bcellss                r+   dry_run_reportr\   x   s3   zz~~o/ABH	/	
9:	(O	
JK	
12$, 5 5w)q'..*0BVHBxjs345
 

01 1( )$,46R	
 7*# %$#	
N(  dqykan-=_Q~M^L__abcd 

>? P! 	PEj\5'*DF4&q}oZ)~NO	PP
 MgYVWXY! 	EE4<KK.!XqU
XKEK! E0q
CDE	E 	abNO	
$%	H[M
"#	
 ! Ls   G
G
c                    t        j                         j                         ddg g g g g g dd
}t        d       	 t	               }	 j                         }|j                  dd      sBt        d       |d   j                  ddd	       t        |       t        j                  d
       t        d|j                  d       d       	 j                         }|j                  dt        j                  j                  dd            }|j!                  d      sd| }||d<   |j                  dd      |d<   t        d| d|d    d|j                  d              t#        |d         }t        j                  j                  dd       }t        d!       i }	t$        D ]R  \  }
}}t'        |
|      }	 j)                  |      }||	|<   |d"   j                  |||d#       t        d$| d%|        T t        d*       d+d,d-dd.d/|d0d1d2d3d4d5dd6d/dd2g}g }|D ]  }	 j+                  |d7   |d8   d9|d:   g ;      }|j                  dd      }|d7   ||d8   |d:   |d<   |d=   |d>   |d?   d@}|j                  |       |dA   j                  |d7   |d   |d8   |d:   dB       t        dC|d7    dD|         t        dH       i }|D ]  }|d   }|d<   }|d=   }|d>   }|d?   }t,        D ]  }| dI| }t/        t0              }	 |r&t3        |||dJ||||K      }|j                  dd      }n*j5                  ||dJ|||d9L      }|j                  dd      }||||f<   |dM   j                  |||dJdN       t        dO| dD|          | st        dR       dS|dT<   nt        dU|  dV       i }t$        D ]  \  }
}}|	j                  |      } | st        dW| dX       *dY| }!t6        j                  |dZ      }"	 j9                  |!| | |"t:        [      }#|#j                  dd      }$|$||<   |d\   j                  |!|$||| d]       t        d^|! dD|$         da}%|D ]  }|d<   }t,        D ]  }|j                  ||f      }|st        db| dI| d       +t$        D 
&cg c]  \  }
}}&|&|k(  s||
f }'}}
}&|'D ]  \  }}(|j                  |      }$|$st        dc| d       )dd| dI| dI| })	 t=        |||$|)|e      }*|*j                  dd      }+|df   j                  |)|+||$dg       |%d
z  }%t        dh|) dD|+           t        dk|% dl       t        dm       t        |       t        dn       t        do       t        dp|d    dq|d           t        drt?        |d"          ds       t        dtt?        |dA          ds       t        dut?        |dM          ds       t        dvt?        |d\          ds       t        dwt?        |df          ds       t        dxt?        |d          dy       |j                  dT      rt        dz|dT           t        d{t@                t        dn       y# t
        $ rY}t        d|        |d   j                  dt        |      d	       t        |       t        j                  d
       Y d}~d}~ww xY w# t
        $ r9}t        d|        |d   j                  dt        |      d	       Y d}~Yd}~ww xY w# t
        $ rY}t        d|        |d   j                  dt        |      d	       t        |       t        j                  d
       Y d}~'d}~ww xY w# t
        $ r=}t        d&| d'|        |d   j                  d(|t        |      d)       Y d}~/d}~ww xY w# t
        $ rC}t        dE|d7    d'|        |d   j                  dF|d7   t        |      dG       Y d}~d}~ww xY w# t
        $ r=}t        dP| d'|        |d   j                  dQ|t        |      dG       Y d}~zd}~ww xY w# t
        $ r=}t        d_|! d'|        |d   j                  d`|t        |      d)       Y d}~d}~ww xY wc c}&}}
w # t
        $ r=}t        di|) d'|        |d   j                  dj|)t        |      dG       Y d}~Ad}~ww xY w)|u(   실제 캠페인 세팅을 실행한다. USDN)
	timestamp
account_idcurrencyimages	campaignsadsets	creativesadserrorsblockedu"   [1] MetaAdsClient 초기화 중...u*   [ERROR] 클라이언트 초기화 실패: rh   init)steperrorr   is_validFu6   [ERROR] 액세스 토큰이 유효하지 않습니다.check_tokenzis_valid=Falseu     토큰 유효 확인 (app_id=app_idrN   u7   [WARN] 토큰 유효성 확인 실패 (계속 진행): r   META_AD_ACCOUNT_IDact_ra   rb   u     계정 확인: u	     통화=u	     상태=account_statusu%   [ERROR] 계정 정보 조회 실패: get_account_infor6   r7   u(   [2] 배너 이미지 업로드 (9장)...rc   )cellrV   
image_hashu     업로드 완료: z  hash=u&     [ERROR] 이미지 업로드 실패 (z): upload_image)rk   rt   rl   u&   [3] 캠페인 생성 (2개, PAUSED)...r8   r9   r:   OFFSITE_CONVERSIONSr<   r=   r>   rA   rH   rI   rJ   rK   r   rB   PAUSEDrD   )r   rB   statusrD   special_ad_categoriesrC   rE   rF   rG   )r   r   rB   rD   rC   rE   rF   rG   rd   )r   r   rB   rD   u     캠페인 생성 완료: z  id=u#     [ERROR] 캠페인 생성 실패 (create_campaign)rk   r   rl   u)   [4] 광고세트 생성 (6개, PAUSED)...rM   i^  )
ad_accountcampaign_idr   rD   	targetingoptimization_goalbilling_eventrG   )r}   r   rD   r~   r   r   ry   re   )r   r   r}   rD   u     광고세트 생성 완료: u&     [ERROR] 광고세트 생성 실패 (create_adsetuA   [5] 크리에이티브/광고 생성 스킵 (--page-id 미지정)uA   Facebook Page 미연결 - 크리에이티브/광고 생성 불가ri   u0   [5] 크리에이티브 + 광고 생성 (page_id=z)...u$     [SKIP] 크리에이티브 스킵 (u   ): image_hash 없음	creative_u>   인카금융서비스 리쿠르팅 — 지금 지원하세요!)r   ru   r4   messagelinkrf   )r   r   rt   rV   ru   u$     크리에이티브 생성 완료: u,     [ERROR] 크리에이티브 생성 실패 (create_creativer   u-     [SKIP] 광고 스킵: 광고세트 없음 (u3     [SKIP] 광고 스킵: 크리에이티브 없음 (ad_)r|   adset_idcreative_idr   r?   rg   )r   r   r   r   u     광고 생성 완료: u      [ERROR] 광고 생성 실패 (	create_adu     총 u   개 광고 생성 완료u   [6] 결과 저장 중...z2==================================================u   실행 완료 요약u
     계정: u
     통화: u     이미지: u   개u     캠페인: u     광고세트: u     크리에이티브: u
     광고: u
     오류: u   건u
     주의: u     결과 파일: )!r   r'   	isoformatr,   r   	Exceptionappendr1   _savesysexitrn   rQ   rs   rO   rP   
startswithr   rR   r3   rv   r{   rT   dictCOMMON_TARGETING"_create_adset_with_promoted_objectr   BRAND_MESSAGESr   LANDING_URL
_create_adlenrU   ),r4   resultcliente
token_infoaccount_infora   r|   r?   image_hash_mapr.   r/   rV   pathhcampaigns_metacreated_campaignscmcampcamp_idrecord	adset_mapcamp_recordrC   rE   rF   rG   
adset_namer~   adsetr   creative_mapimg_hashcreative_namer   creativer   ad_countrZ   brand_cellsrM   ad_name	ad_resultad_ids,                                               r+   runr      s
    \\^--/F  ,-
J'')
~~j%0HI8##]EU$VW&MHHQK-jnnX.F-GqIJ..0!%%dBJJNN;OQS,TU
$$V,
|,J)|)--j%@z
|9VJ5G4H	R^RbRbcsRtQuvw 6,/0Jzz~~o/ABH
 23 &(N$, 	a 5'8,	a##D)A'(N8$8##XUV$WX&xjs;<	a 01 1( -$,46R	
 7*# %$#	
N* %'  f	f))Z[//&( * D hhtR(G6
_ "> 2GzNi=#%&7#8	F $$V,;&&"6N ,!'!4$*>$:	 -bj\wiHI; fJ 34 -/I( 6gd#G$z*i(%&78! /	gE!7!E7+J -.I
&g">#-$+'%("+*2&-(7	E  %yyr2H"//$+'%("+*2&-' 0 E  %yyr2H,4	5%.)x '' *&'.(+	 4ZLhZPQW/	g6gv OP_y>witLM (*(0 	h$GXu%))(3H:8*DXYZ'z2M$((0pqGh!11&'##$ 2  'll44)4X&{#** -) (!&&. :=/{m\]7	hB , '	iK(E% %i$==%8GwaPUwVWXY PXff7Kw![\`e[e'2ff#. iKHa"."2"28"<K&QRZQ[[\]^  #E7!E7!H:>Gi$.'1%-(3!(%-%	 !*dB 7u,,(/&+,4/:	 !A6wiuUGLM3i%i'	iR 	fXJ678
 "#	&M M*VL)**VJ5G4HIJ-F8,-.c23-F;/0156
3vh/0156
 VK%8!9 :#>?*S'(,-*S)*+3/0zz)j	*+,-
K=)*MK	  8<=x#a& ABf	  JEaSIJxQ HIIJ  3A378x);c!f MNf	4  	a8
#aSIJ8##^XX[\]X^$_``	a|  	f5bj\QCHI8##->6
]`ab]c$dee	f@  g<ZLA3OPx ''^abc^d(effgZ  hB=/QTUVTWXYx ''1BH_bcd_e(fggh g8 % i>wis1#NOx(//g`cde`f0ghhis   
Y> B[# B\( 3?^B_B`%>Aa.#b72b73Ab>>	[ A[[ #	\%,.\  \%(	^
1A^^
	_2__	`"8``"%	a+.2a&&a+.	b472b//b4>	d	2c?	?d	r|   r}   r   rD   r~   r   r   rG   c                    ddl m} |j                  j                  ||j                  j                  ||j                  j
                  ||j                  j                  ||j                  j                  ||j                  j                  ||j                  j                  d|j                  j                  |i}	| j                  g |	       t        dfd             }
|
S )u   
    promoted_object가 필요한 광고세트를 facebook_business SDK로 직접 생성한다.
    MetaAdsClient.create_adset은 promoted_object 파라미터를 지원하지 않으므로
    AdAccount.create_ad_set을 직접 호출한다.
    r   )AdSetrx   fieldsparamsexport_all_datac                      t               S Nr   )adset_results   r+   <lambda>z4_create_adset_with_promoted_object.<locals>.<lambda>  s    $|BT r-   )!facebook_business.adobjects.adsetr   Fieldr   r}   rD   r~   r   r   ry   rG   create_ad_setgetattr)r|   r}   r   rD   r~   r   r   rG   r   r   datar   s              @r+   r   r     s     8 	$  ,y%%'8!!=H##_	F ++2f+ELU'8:TUWDKr-   r   r   r?   c                 p   t         j                  j                  |t         j                  j                  |t         j                  j                  d|it         j                  j
                  dt         j                  j                  dg|gdgi}| j                  g |       t        dfd             }|S )u   
    facebook_business SDK의 AdAccount.create_ad를 사용하여 광고를 생성한다.
    MetaAdsClient에 create_ad 메서드가 없으므로 SDK를 직접 사용한다.
    r   rx   offsite_conversion)zaction.typefb_pixelr   r   c                      t               S r   r   )r   s   r+   r   z_create_ad.<locals>.<lambda>9  s    tI r-   )	r   r   r   r   r   ry   tracking_specsr   r   )r|   r   r   r   r?   r   r   r   s          @r+   r   r   !  s     	t
8
M;7

 45%J"
F $$Bv$>IO$57NOQDKr-   r   c                     t         j                  dd       t        t        dd      5 }t	        j
                  | |dd       d	d	d	       t        d
t                y	# 1 sw Y   xY w)u&   결과를 JSON 파일로 저장한다.T)parentsexist_okwzutf-8)encodingF   )ensure_asciiindentNu   결과 저장 완료: )
OUTPUT_DIRmkdiropenrU   jsondumpr,   )r   fs     r+   r   r   B  sZ    TD1	k3	1 ;Q		&!%:;
 ./; ;s   AA(c                      t        j                  dt         j                  t              } | j	                  ddd d       | j	                  ddd	d
       | j                         S )Nu=   Meta Marketing API 리쿠르팅 캠페인 셋업 스크립트)descriptionformatter_classepilogz	--page-idPAGE_IDu`   Facebook 페이지 ID. 지정 시 크리에이티브/광고 생성 포함. 미지정 시 스킵.)metavardefaulthelpz	--dry-run
store_trueFu7   실제 API 호출 없이 실행 구조만 출력한다.)actionr   r   )argparseArgumentParserRawDescriptionHelpFormatter__doc__add_argument
parse_args)parsers    r+   r   r   O  st    $$S <<F
 o	   F	   r-   __main__)r4   )-r   r   r   rO   r   r   pathlibr   
_WORKSPACEr1   r   insertutils.meta_ads_clientr   facebook_business.adobjects.adr   %facebook_business.adobjects.adaccountr   ImportErrorr   r)   r   r   rU   r   r2   rR   rT   r   r   r,   r3   r\   r   intr   r   r   r   	Namespacer   __name__argsdry_runr4    r-   r+   <module>r      s     	 
   '(
z?#(("HHOOAs:' /1? >?
66.@A
 E
 	FOFOFOFO	
  &9&9&9&?		
	 	a+ 8 Z
y NS T M Ms Ms M:"C$J :"4 :"Dzt z zD
  	
     
J  	
  
B0$ 04 0H&& * z<D||t||, G  	Fqc
JKCHHQKKs   $E8 8F%=F  F%