From 82497ffc6468efd30972e41a1b27e32b4d80def8 Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Fri, 29 Aug 2025 15:20:59 +0300 Subject: [PATCH] provider enable/disable fix --- .../virtualGame/veli/game_orchestration.go | 26 ++++- internal/services/virtualGame/veli/service.go | 97 +++++++++++++++--- internal/web_server/handlers/bet_handler.go | 2 +- internal/web_server/handlers/veli_games.go | 39 +++++++ static/logos/popok-dark.png | Bin 0 -> 8594 bytes static/logos/popok-light.png | Bin 0 -> 8148 bytes 6 files changed, 143 insertions(+), 21 deletions(-) create mode 100644 static/logos/popok-dark.png create mode 100644 static/logos/popok-light.png diff --git a/internal/services/virtualGame/veli/game_orchestration.go b/internal/services/virtualGame/veli/game_orchestration.go index b2187b0..b2ba665 100644 --- a/internal/services/virtualGame/veli/game_orchestration.go +++ b/internal/services/virtualGame/veli/game_orchestration.go @@ -44,16 +44,36 @@ func (s *Service) AddProviders(ctx context.Context, req domain.ProviderRequest) createParams := dbgen.CreateVirtualGameProviderParams{ ProviderID: p.ProviderID, ProviderName: p.ProviderName, - LogoDark: pgtype.Text{String: p.LogoForDark, Valid: true}, - LogoLight: pgtype.Text{String: p.LogoForLight, Valid: true}, + LogoDark: pgtype.Text{String: p.LogoForDark, Valid: p.LogoForDark != ""}, + LogoLight: pgtype.Text{String: p.LogoForLight, Valid: p.LogoForLight != ""}, Enabled: true, } if _, err := s.repo.CreateVirtualGameProvider(ctx, createParams); err != nil { - // Log error but continue with other providers return nil, fmt.Errorf("failed to add provider %s: %w", p.ProviderID, err) } } + // 4. Always add "popok" provider manually + popokParams := dbgen.CreateVirtualGameProviderParams{ + ProviderID: "popok", + ProviderName: "Popok Gaming", + LogoDark: pgtype.Text{String: "/static/logos/popok-dark.png", Valid: true}, // adjust as needed + LogoLight: pgtype.Text{String: "/static/logos/popok-light.png", Valid: true}, // adjust as needed + Enabled: true, + } + + if _, err := s.repo.CreateVirtualGameProvider(ctx, popokParams); err != nil { + return nil, fmt.Errorf("failed to add popok provider: %w", err) + } + + // Optionally also append it to the response for consistency + // res.Items = append(res.Items, domain.VirtualGameProvider{ + // ProviderID: uuid.New().String(), + // ProviderName: "Popok Gaming", + // LogoForDark: "/static/logos/popok-dark.png", + // LogoForLight: "/static/logos/popok-light.png", + // }) + return &res, nil } diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index c8da008..292f4e9 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -62,39 +62,102 @@ func (s *Service) GetProviders(ctx context.Context, req domain.ProviderRequest) } func (s *Service) GetGames(ctx context.Context, req domain.GameListRequest) ([]domain.GameEntity, error) { - sigParams := map[string]any{ - "brandId": req.BrandID, "providerId": req.ProviderID, + // 1. Check if provider is enabled in DB + provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID) + if err != nil { + return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err) } + + if !provider.Enabled { + // Provider exists but is disabled → return empty list (or error if you prefer) + return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) + } + + // 2. Prepare signature params + sigParams := map[string]any{ + "brandId": req.BrandID, + "providerId": req.ProviderID, + } + + // 3. Call external API var res struct { Items []domain.GameEntity `json:"items"` } - err := s.client.post(ctx, "/game-lists/public/games", req, sigParams, &res) - return res.Items, err + if err := s.client.post(ctx, "/game-lists/public/games", req, sigParams, &res); err != nil { + return nil, fmt.Errorf("failed to fetch games for provider %s: %w", req.ProviderID, err) + } + + return res.Items, nil } func (s *Service) StartGame(ctx context.Context, req domain.GameStartRequest) (*domain.GameStartResponse, error) { - sigParams := map[string]any{ - "sessionId": req.SessionID, "providerId": req.ProviderID, - "gameId": req.GameID, "language": req.Language, "playerId": req.PlayerID, - "currency": req.Currency, "deviceType": req.DeviceType, "country": "US", - "ip": req.IP, "brandId": req.BrandID, + // 1. Check if provider is enabled in DB + provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID) + if err != nil { + return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err) } + + if !provider.Enabled { + // Provider exists but is disabled → return error + return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) + } + + // 2. Prepare signature params + sigParams := map[string]any{ + "sessionId": req.SessionID, + "providerId": req.ProviderID, + "gameId": req.GameID, + "language": req.Language, + "playerId": req.PlayerID, + "currency": req.Currency, + "deviceType": req.DeviceType, + "country": "US", + "ip": req.IP, + "brandId": req.BrandID, + } + + // 3. Call external API var res domain.GameStartResponse - err := s.client.post(ctx, "/unified-api/public/start-game", req, sigParams, &res) - return &res, err + if err := s.client.post(ctx, "/unified-api/public/start-game", req, sigParams, &res); err != nil { + return nil, fmt.Errorf("failed to start game with provider %s: %w", req.ProviderID, err) + } + + return &res, nil } + func (s *Service) StartDemoGame(ctx context.Context, req domain.DemoGameRequest) (*domain.GameStartResponse, error) { - sigParams := map[string]any{ - "providerId": req.ProviderID, "gameId": req.GameID, - "language": req.Language, "deviceType": req.DeviceType, - "ip": req.IP, "brandId": req.BrandID, + // 1. Check if provider is enabled in DB + provider, err := s.repo.GetVirtualGameProviderByID(ctx, req.ProviderID) + if err != nil { + return nil, fmt.Errorf("failed to check provider %s: %w", req.ProviderID, err) } + + if !provider.Enabled { + // Provider exists but is disabled → return error + return nil, fmt.Errorf("provider %s is disabled", req.ProviderID) + } + + // 2. Prepare signature params + sigParams := map[string]any{ + "providerId": req.ProviderID, + "gameId": req.GameID, + "language": req.Language, + "deviceType": req.DeviceType, + "ip": req.IP, + "brandId": req.BrandID, + } + + // 3. Call external API var res domain.GameStartResponse - err := s.client.post(ctx, "/unified-api/public/start-demo-game", req, sigParams, &res) - return &res, err + if err := s.client.post(ctx, "/unified-api/public/start-demo-game", req, sigParams, &res); err != nil { + return nil, fmt.Errorf("failed to start demo game with provider %s: %w", req.ProviderID, err) + } + + return &res, nil } + func (s *Service) GetBalance(ctx context.Context, req domain.BalanceRequest) (*domain.BalanceResponse, error) { // Retrieve player's real balance from wallet Service playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64) diff --git a/internal/web_server/handlers/bet_handler.go b/internal/web_server/handlers/bet_handler.go index a323560..fb59223 100644 --- a/internal/web_server/handlers/bet_handler.go +++ b/internal/web_server/handlers/bet_handler.go @@ -149,7 +149,7 @@ func (h *Handler) CreateBetWithFastCode(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusInternalServerError, "Failed to create bet:"+err.Error()) } - wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), bet.UserID) + wallet, _ := h.walletSvc.GetCustomerWallet(c.Context(), bet.UserID) // amount added for fast code owner can be fetched from settings in db settingList, err := h.settingSvc.GetSettingList(c.Context()) diff --git a/internal/web_server/handlers/veli_games.go b/internal/web_server/handlers/veli_games.go index 8a005e6..0f299f5 100644 --- a/internal/web_server/handlers/veli_games.go +++ b/internal/web_server/handlers/veli_games.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log" + "strings" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/virtualGame/veli" @@ -70,6 +71,7 @@ func (h *Handler) GetGamesByProvider(c *fiber.Ctx) error { }) } + // Default brand if not provided if req.BrandID == "" { req.BrandID = h.Cfg.VeliGames.BrandID } @@ -77,6 +79,16 @@ func (h *Handler) GetGamesByProvider(c *fiber.Ctx) error { res, err := h.veliVirtualGameSvc.GetGames(context.Background(), req) if err != nil { log.Println("GetGames error:", err) + + // Handle provider disabled case specifically + if strings.Contains(err.Error(), "is disabled") { + return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ + Message: "Provider is disabled", + Error: err.Error(), + }) + } + + // Fallback for other errors return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Message: "Failed to retrieve games", Error: err.Error(), @@ -91,6 +103,7 @@ func (h *Handler) GetGamesByProvider(c *fiber.Ctx) error { }) } + // StartGame godoc // @Summary Start a real game session // @Description Starts a real VeliGames session with the given player and game info @@ -119,7 +132,10 @@ func (h *Handler) StartGame(c *fiber.Ctx) error { }) } + // Attach user ID to request req.PlayerID = fmt.Sprintf("%d", userId) + + // Default brand if not provided if req.BrandID == "" { req.BrandID = h.Cfg.VeliGames.BrandID } @@ -127,6 +143,16 @@ func (h *Handler) StartGame(c *fiber.Ctx) error { res, err := h.veliVirtualGameSvc.StartGame(context.Background(), req) if err != nil { log.Println("StartGame error:", err) + + // Handle provider disabled case specifically + if strings.Contains(err.Error(), "is disabled") { + return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ + Message: "Provider is disabled", + Error: err.Error(), + }) + } + + // Fallback for other errors return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Message: "Failed to start game", Error: err.Error(), @@ -141,6 +167,7 @@ func (h *Handler) StartGame(c *fiber.Ctx) error { }) } + // StartDemoGame godoc // @Summary Start a demo game session // @Description Starts a demo session of the specified game (must support demo mode) @@ -161,6 +188,7 @@ func (h *Handler) StartDemoGame(c *fiber.Ctx) error { }) } + // Default brand if not provided if req.BrandID == "" { req.BrandID = h.Cfg.VeliGames.BrandID } @@ -168,6 +196,16 @@ func (h *Handler) StartDemoGame(c *fiber.Ctx) error { res, err := h.veliVirtualGameSvc.StartDemoGame(context.Background(), req) if err != nil { log.Println("StartDemoGame error:", err) + + // Handle provider disabled case specifically + if strings.Contains(err.Error(), "is disabled") { + return c.Status(fiber.StatusForbidden).JSON(domain.ErrorResponse{ + Message: "Provider is disabled", + Error: err.Error(), + }) + } + + // Fallback for other errors return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Message: "Failed to start demo game", Error: err.Error(), @@ -182,6 +220,7 @@ func (h *Handler) StartDemoGame(c *fiber.Ctx) error { }) } + func (h *Handler) GetBalance(c *fiber.Ctx) error { var req domain.BalanceRequest if err := c.BodyParser(&req); err != nil { diff --git a/static/logos/popok-dark.png b/static/logos/popok-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..71c9e5026c4e46069e8b1d276f2f18444c992442 GIT binary patch literal 8594 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 literal 0 HcmV?d00001 diff --git a/static/logos/popok-light.png b/static/logos/popok-light.png new file mode 100644 index 0000000000000000000000000000000000000000..40f051b75e1acdf6c1616cf1e08ce8897e3ba522 GIT binary patch literal 8148 zcmY*;Wmr_t`}eYRNi4NAEGfODbS_ADHwY3EBHdj}i3mzJ(jXuq4POwXJET-Pq$T%% z{JnUt>)983&6z!W&zw0k_x*`@ZB1oDJZd};2t@c?MNt<7LW=_05jbGr``al84+umI zdafv^?}v71fvZnu)P9^i@JGnm9F#;P#p@Yp%g<{wdzvHWCh#GWk9Rl@-z{rbCtv(F z`9I9y*UT`7Ki?D~k>fATNx%2T$0tC&X({#nNV6!JJmc-Rz|sPE^g2!J3qK}jE_5%A zE^8{w9`4L5-GAJzqt@=`YEB-nyXOW4DIWR3NDyoT8%NS^iwM?(!w(J#1Er@im}9ts zfy*p)b%Z3IkX%>>9IQ%W4cASe8WjX1!`N{6p-x~ft``_oXr(}#QwjMSiiVJdV5r!a zJRhrmM`8{9!j6%J%<0+S00L3P0#Q8|y~h5H76r5+RJ3zYwAy465Bacf!?*;=a3HKF z5DFSAweTI7hbGW|7T&9a2qWQtg63&&Z_d!_09@dJfT-XN=GT-|;j*$}-#CbCnXkkD zUpJNyWvj0iK3E;0HJY&<0tbgAhoRo;7#J9sn3!;eQlTMwa4t{jo$7 zxe{MqjJ}_czH|NTFeZP~4-AfA4C|P174bb<_8v?iFW1bS4$peS2CI{*tf){?RRyCF z(uu$6k0BIwU%PlZ0tFtbfD#-X9bIKuv%lUg6X$fkJ@(P?fYSb5TH5LT`2?RuyZ%gV z<7;64e=x&82s%c*eeLBHti!A`?+5=V9ekT5V8`-t*yP{p$lzQY|B&F}(MY_aQb*Kv zKiBhTItZ#H-WcDH?>Suhys1xOS)68%k-fXo8`;+}fcJ0lT`)HENsi^=NaO~) zZMMn*N&hVr?T9!G|5HJMY(a?KFixQ3Av>ubxd}PU8V4*OAOJoPjzQ|`>9OAS{2N-E zow(Iwvv0FMR!Mk{3f>o?r;ly?Z&y&o^_=}4Rhn}HLHEvu$~nUR*Q>&qnwx83YWkE( zb^;!TOs11SxHG81!F|NmO3REM_kTT^5(pWKiPE9?Dfz)R-v{)CM*~~u zbm6mHBvvFgQJ&#>Jn%_KfOna!f)TP-z|OcBTro65JwE8^>T2dmn&|4TCpin&EjMmF2zH1mW`Jd9-P=g%+h>&a;t zwtb|}t$Cgpq@|^4huoa~UR*SuJy0<0cK8VY=J=hcXPf|Xdb6YFl6%CA^h#^j~)4CS$$Fx5;VhzX9Z247LK!|W$YT} z&j#$@5)x^Is_>(jEYTPU_Z@ieCRKgTMvj_gggQUZ&C|PNBX90DV;N+x2iht5EY*Wp zu|)bl3cFAMALlzp&1!jk0XCSCzCb5~mto?Ptq+B<-E4){^pKL0vM3GMM;z8)VnNiA;yleg zTG$g(xhbSFU&t()vvy$@@wnXQ@(O~x4lk+`!ox#85xSPx81#$$_qC)cpgOM} zv#jYI-x+`JJ_bW!%1|<0Bp(Hds^5#7Zf#1v@qO274BJZb)VK|9Hku*u(ZqY$I<*6Z z4P$T7yH{lvvY3#!X$HWtTc0i*~YXA~EKviXMBGNXc#1?=*0E&87U{E2yEC zCa$g>n)9N~|j#AD@3Fs)dX2 zlH~ZwkM;HZ>R*CyFFpv^A$G=dlj!8t5v|N{CTZy|;8erqTU=e!lIf+EDTFsbYAa!; z8AkC-d0pKtsIR|s$J)yD}fy>NL z3D`!R9dK4F%BD?y(ku7zN71$fE2bg7!z7q`n84$~)!z}XDFKUHqm z?D#d3!F5D3avE88`5Q(9xioR0g7lIOJ>p_Qhplt|HnuqbXcs1!Tfg5HRZgab3OW$v zEKNG;<8Z+H_9^QF@D*O=(@O-L;(kITmbGOBYqL#1 zR%+Z&DFT0g|6Lpem8(M&TzGAPTY`uuq~t7b0-TpCUyB;>yK6d6+**ZlLtE;_sLCy0hkg|S%GkiIqQI2Q<7vw7?(Y5l{qc{E{`7}_A{klsv^%u)ts_^dr%xb;Fvv4kUa^zF`j~N5fc73EfeU#q*97@T| zz_uk#RV73H$P}6XTN(2<2-?SiPc)@8T4zYLSBr2g*T_bks>zeekwN6}{b0ou-HE`&Hzzg7>C08;R3a;`&}BcrT(BCYIGnG^u0}S=ZtcA1 z#pkJ64~b}And)AN!^Lzpw*yN(!)Ezdje$`&gKhHOirwVnczuu5n=4X0D(J?uwa!4% z22B1KukXoq62Ya>5z3Ya}$1m7K zEYP*3NRbUjZ7%W&+YN=u#LN4n!>C35Q>5{MC?6luA)7%ykNZYXc#FH;lLY$D{AKYm z^VAd+KF6!iMmFRGH`%8n;tQbC#c42Q5$9Q)V$2RF0Dh|oJ>`cQz_d)@t6jkzrtRDK z?_X)m9{3s<%whFOTz`Fk{qA&-(#lV@HiLqcG=jv6>-i95Yvb4~~0R?3Eq7*Lm4S5TW*?YJjfv2zdS%J*X}8}0d*>p|xl-gH}Cab*-f zuLkEnsY7Us8Zl{!az|CMvSgn>$IS_NrwOE8H3>F(bkk;MVekFfcnEu+bi}puT&2PP z=4z5Qv@azoxk{aW?{!TZ_{OPYcbE;O@}sn1fYa?~V3zC@}BMUt)9n9(Pm znxVro$=v!_l4)AEe>vR}PSRl&c*9qBEw?DsM5(D)+|G-9d98Z>UjmsklexJ$L2)zU z^XxTHDr{O*p8w5>Wkms0phwQQj+LGL^Te>DVz8mCfRL)1+R1wNi8(n3vT+-=vi`Vj zuBxB&nq99|S3fZ4vZSO$d!A9k=g@<55nW}+^w6a=J;s%WtgYI&{JF)-z?<6z zIX)3(zhVouoN@oQhm%|HQMw1U(b>)D;!xax0O5gtpQwEiZM?pntn_q+4_AlFt2~~8 z?TbIlG_pOIii(Obq`OLLqg|mNJa(*2Q=^Hz;t~l?jiMbV^1L}Yel-Ee-f%*Vuakuf zX3i)s{_zaGiXj6vzh3%D!R!kNJKX&7V2+nD*N`wz&_Jq|{19`MR6^7VCtxI`O6D)V z!#fMrSlG)7qdLor1X2j3x#bmr{v~}57e_`$fP}WQvlDu?jof;-@kt=Iw(W*Px$+&!Gyi-4b*blzwAI#tPns`LbP7nN8-`iQ26Hva9rO2kVSu1g;Q7QFJ;-}&!5Ez(~WpT{{hGz zDcU$)YIH`7YYQ@}-=gmu$y9?@WM0C2DkLm98_?{if~y{hvpk&oa6 zq)?y7_V*9eP9frRwnNmA#-c8}bNq2SE(qt-GWphh&zMczFHjEa%GhA!YvD&HRqm!8%5?C~vW-HZ3#+29RP^hx@)jyrN zEL&YpuJ;|M9+?6530$Q6!kRhov#Xj&=uenye=oj`dqEGZh2*<~;MWj*N?v42O00xI z!Ryk>EMy6>M9|;*0X@e>;*6)Rqb^I0rAKD>;b(W`+`l=HoL!!lLbehN^m=?B^3vD$ zma*1eJY-drk=Ia*PC9mCvCC@Y!q?T_bML9%@d?8davC?2mRmhxhGT_=g;hp%d6NE( zdSz=m0E~~b6K3j+5}DsEK&s(c1RQnm5{T6RpsHBqfNPAVoDeygA77o+RI|`@<(T>Y zy)^3ne9`L5yvkq5<9Y>vAw)ekdM+;BxW)j`XR61Sr{G#00qPZgmyrQ*OidqQIsZfA zs^Cnk&NjoctDG}6eUw1r+qZ86u8(V)>h5B%bo&+MF4b`=2$V6D2k3`51s3m@7W-fc zT;%AGUV1ssL&x42B}I{_*w{#6`@vmsZ*MOEi$FAeiLtWK={LVsSwA41nXs~-@$l3D z#p&I3KWQE9X*8;NSx#(kt+S(g_If>(kE^&@7JI^o2loXJZAA%6;c$L?`GiaFd zY4`4SW?asAb2)1+6$gI@UZeg#Xr-r6er-M2b=L}Z0bsb7P5_=f29GYw9-LP_Dxv_4 zlvU)|OxKbwjMK62kp@(x4!=`dRaLxXlY40xo-^48wM^d41YNM?Ojg4tIiNK`;TYEh z>VULhugWtH&*Zc6k-nrF`nYP>&KJL!7Tv~FB{vuuJubfKUKWN6-DvYYvD|309ZUeK zFIY>g(!f|DdAT9ey_8_6&D{Q1ua~>S@3nwG+M1J1wHtWfOT8A{^)8L1TBeJtEgL~> z^>J}HzMe?gEnC9O$6w#lYpL~fhXw|I?d{@X*co&E=Tyz6hU8ydMH^GO1LU|11~ zgR4eW=eWHqcTY&*(0Uwoziy#)d(_u0?ok_OT)*FNDaRftz>v%z)T2n6X!zI7+JV%? zxWk9%1w_Gg1<;uQ$wi<%;)e&>0bOS8vXp4S@Nj3KX?Yluf8pCa-2cqp1lrz{^UTY- zVszxr_VnA+1^u3Y9lelq5`Fi6(ag^w?WOGk=vVoLnHE%2$-IX;q#|(`kfp?)?--6t zKO2>+lV7|j!K{xIu^qsvCbjPV87CFul`%d}ceytid{V5r*_W>LMez~3y9`xAq=yTmPg2t2FjxtlJLZ5}GE@pZ}$%5<5YE^<|zEUsJ0R z;M&Dq2WfXU;SY9wnN)Qo%%cdnS5B3q%R<-cE*ab*aX}_g{ zc`iLuX%;Ydk}9Uv8#10n@zS{G!`6#HJynzMNyD`2f?0lHt`l-se{EqTWtq;t-Q9cK z7L;pGi#bHm=6*`0OT7I~wRMXJZe)#Y~kqyjat4|_)mu+m-XyveC zaGCw`xU{@?JmyBn=UZH5qvjva`Y#@Y?5-IBvr+FIxL3P9#l@80s zv*iw7J|$9keH9=MK2cK+5b19xSUWvCI|F3Fcg$EyLdvtBvW@-vtrPljVvzkiPfK0_ zX_vh{DN0}22=2ZnS;W8nw4L?$B<|Gxfq<8 znHkWFc_{E{SB%f?bo+i`U40uK?h!K3)s4uIyix_Y@os56{o92)tNh&DBc|eHmS!v{ z$;W_e9dbFc#E8Z%Wl*2LY4PWuI0QT4dM~85)Vw;2@qLG=2c$ z(~Rfs;c7QVbdJ4(vd-5jM%ZvFFLnEVI+*>Wm$hY1dNA+w(=n2Ru*>gSS_#RU9+-Gb z;9G?y{m@SykWTxSO8+8Pn1Jap`Ps+_qF&vs)q|-_A+V=}!%R^EjSZHzxc&S0zYIAn?tDG%_gFGI5O!7M8OF0n=@O+-n; z<&TPOX`PA;htsd&G%EQ!SDDv_`i2Gk>1ne3RR%V@yG{&%(rZm+urOv-Nm6iX{qwdY zx@6zbkE`NkLrjLWCj$%)C$&`LMt5IrP0Pz0pMd)JcRwlbmv&z7>OASr&WyHRsI`y^ zxJV_pL-WUYS!dR9SFdWBAwmkMDWzO%?cQ8UR8J1uympS)yHOn{U0mGUUp;!u*Kc*4 z-=*4&s(@+p8*9zmBIf=YE@x@$=zTa+`Y}LB^J&suorD^Ou>g^DGo#wX)lJ}c5W5{W z)z;S5)J%3I2P!5ZK7QmjfI!*U*=MXOSgK5-!!{Bti!j42>;X9lP{RhKGZ)?Zrc{D- zzAOeb&->&yY8YM`w^Ft#z^2``Cm(bbI@J;=Tn0j$DQIa|3*^w%b9R4tP3(0V#i`N( zN_d(M-|cJ0Qt=2LkwdSCEz0f!Etj?ZzGZqLdy(utzWuARkwu%G8iN7sxVRLUf7PJW zLncplJ9~h7L)Y}W9u_x=*0<(q4tO)3pEy(|`l|mMv@Y{;1@SouXaxQC*aKqhwH8r&LR~hC72>N#WR0x$+h z_{I3)`~!q;tVRIo|*SCxFQs1Z{q`VpzB4DJ6tqoIvsi zEu7LSXexFZ#qA7&0$Ar){-qZZ@DKOJz!vRqNT$)zhll?RL8CxJs{h=(WYCr6AqhhY zR_YX>kp{U6Q91(75=yQBV2+m5#oS9EI35uZgLF`9qeG(*U|#)Y1&?O(QO@zA594T9 zwYWl)X@NKewJ?4l>*-kV#~=YsixL8XRT5Rl0^<_MF|x9jI_d3C+}fMHB!Cv0FofO< zB-8Fc0k7&J3MjyU;c~t+QJ3*+CEmtS__GRXMhD8hJq2((OFnp)^1rj z`5%0Q{ji41OVrPV-kiPg>m%OzXUsk#0f>xna9|7KHWixogEwB|WKC)SzN%=hKDHv_AetJmu~_|MIi8pxlWh=cpNamq`)-7zE-16avt~T0mk!` zp6|D|eq>lEbC3P6{Do+