admanagekit-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/content/README.md +851 -0
  2. package/content/docs/AD_FREQUENCY_CONTROL.md +450 -0
  3. package/content/docs/AD_LOADING_STRATEGIES.md +369 -0
  4. package/content/docs/API_REFERENCE.md +734 -0
  5. package/content/docs/APP_PURCHASE_GUIDE.md +704 -0
  6. package/content/docs/BANNER_AD_IMPROVEMENTS.md +325 -0
  7. package/content/docs/COMPOSE_INTEGRATION.md +477 -0
  8. package/content/docs/CONFIGURATION_USAGE.md +168 -0
  9. package/content/docs/INTERSTITIAL_BUILDER_GUIDE.md +518 -0
  10. package/content/docs/JAVA_USAGE_GUIDE.md +785 -0
  11. package/content/docs/LOADING_STRATEGY_EXAMPLES.md +463 -0
  12. package/content/docs/NATIVE_AD_MANAGER_ENHANCEMENTS.md +347 -0
  13. package/content/docs/NATIVE_AD_PRELOADING.md +467 -0
  14. package/content/docs/NATIVE_TEMPLATE_VIEW.md +413 -0
  15. package/content/docs/app-open-ads.md +412 -0
  16. package/content/docs/interstitial-ads.md +269 -0
  17. package/content/docs/native-ads-caching.md +182 -0
  18. package/content/docs/release-notes/RELEASE_NOTES_v2.1.0.md +156 -0
  19. package/content/docs/release-notes/RELEASE_NOTES_v2.2.0.md +248 -0
  20. package/content/docs/release-notes/RELEASE_NOTES_v2.3.0.md +280 -0
  21. package/content/docs/release-notes/RELEASE_NOTES_v2.5.0.md +73 -0
  22. package/content/docs/release-notes/RELEASE_NOTES_v2.6.0.md +265 -0
  23. package/content/docs/release-notes/RELEASE_NOTES_v2.7.0.md +168 -0
  24. package/content/docs/release-notes/RELEASE_NOTES_v2.8.0.md +153 -0
  25. package/content/docs/release-notes/RELEASE_NOTES_v2.9.0.md +479 -0
  26. package/content/docs/release-notes/RELEASE_NOTES_v3.0.0.md +343 -0
  27. package/content/docs/release-notes/RELEASE_NOTES_v3.1.0.md +131 -0
  28. package/content/docs/release-notes/RELEASE_NOTES_v3.3.0.md +252 -0
  29. package/content/docs/release-notes/RELEASE_NOTES_v3.3.2.md +215 -0
  30. package/content/docs/release-notes/RELEASE_NOTES_v3.3.3.md +116 -0
  31. package/content/docs/release-notes/RELEASE_NOTES_v3.3.4.md +142 -0
  32. package/content/docs/release-notes/RELEASE_NOTES_v3.3.5.md +94 -0
  33. package/content/docs/rewarded-ads.md +623 -0
  34. package/content/wiki/Ad-Loading-Strategies.md +209 -0
  35. package/content/wiki/App-Open-Ads.md +189 -0
  36. package/content/wiki/Banner-Ads.md +133 -0
  37. package/content/wiki/Billing-Integration.md +69 -0
  38. package/content/wiki/Configuration.md +252 -0
  39. package/content/wiki/Consumable-Products.md +166 -0
  40. package/content/wiki/Home.md +144 -0
  41. package/content/wiki/Interstitial-Ads.md +313 -0
  42. package/content/wiki/Jetpack-Compose.md +270 -0
  43. package/content/wiki/NativeAdManager.md +220 -0
  44. package/content/wiki/Purchase-Categories.md +94 -0
  45. package/content/wiki/Rewarded-Ads.md +420 -0
  46. package/content/wiki/Subscription-Upgrades.md +278 -0
  47. package/content/wiki/Subscriptions.md +368 -0
  48. package/content/wiki/_Footer.md +3 -0
  49. package/content/wiki/_Sidebar.md +18 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.js +6 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/server.d.ts +1 -0
  54. package/dist/server.js +18 -0
  55. package/dist/server.js.map +1 -0
  56. package/dist/tools/code-generation.d.ts +2 -0
  57. package/dist/tools/code-generation.js +264 -0
  58. package/dist/tools/code-generation.js.map +1 -0
  59. package/dist/tools/documentation.d.ts +2 -0
  60. package/dist/tools/documentation.js +202 -0
  61. package/dist/tools/documentation.js.map +1 -0
  62. package/dist/types.d.ts +18 -0
  63. package/dist/types.js +83 -0
  64. package/dist/types.js.map +1 -0
  65. package/dist/utils/doc-loader.d.ts +17 -0
  66. package/dist/utils/doc-loader.js +245 -0
  67. package/dist/utils/doc-loader.js.map +1 -0
  68. package/dist/utils/search.d.ts +2 -0
  69. package/dist/utils/search.js +62 -0
  70. package/dist/utils/search.js.map +1 -0
  71. package/dist/utils/templates.d.ts +60 -0
  72. package/dist/utils/templates.js +793 -0
  73. package/dist/utils/templates.js.map +1 -0
  74. package/package.json +51 -0
@@ -0,0 +1,518 @@
1
+ # InterstitialAdBuilder - Complete Guide
2
+
3
+ ## ✨ Overview
4
+
5
+ The `InterstitialAdBuilder` provides a modern, fluent API for loading and showing interstitial ads with advanced features like fallback support, automatic retry, and customizable callbacks.
6
+
7
+ ---
8
+
9
+ ## 🚀 Basic Usage
10
+
11
+ ### 1. Simple Show
12
+ ```kotlin
13
+ InterstitialAdBuilder.with(this)
14
+ .adUnit("ca-app-pub-xxxxx/yyyyy")
15
+ .show {
16
+ // Continue with next action
17
+ startNextActivity()
18
+ }
19
+ ```
20
+
21
+ ### 2. Using Extension Function
22
+ ```kotlin
23
+ showInterstitialAd("ca-app-pub-xxxxx/yyyyy") {
24
+ startNextActivity()
25
+ }
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 🔧 Builder Methods
31
+
32
+ ### Configuration Methods
33
+
34
+ | Method | Description | Example |
35
+ |--------|-------------|---------|
36
+ | `.adUnit(String)` | Set primary ad unit ID (**required**) | `.adUnit("ca-app-pub-xxx/123")` |
37
+ | `.fallback(String)` | Add single fallback ad unit | `.fallback("ca-app-pub-xxx/456")` |
38
+ | `.fallbacks(vararg String)` | Add multiple fallback units | `.fallbacks("unit1", "unit2", "unit3")` |
39
+ | `.force()` | Show ad ignoring time interval | `.force()` |
40
+ | `.respectInterval(Boolean)` | Respect time interval (default: true) | `.respectInterval(false)` |
41
+ | `.timeout(Long)` | Set load timeout in milliseconds | `.timeout(5000)` |
42
+ | `.timeoutSeconds(Int)` | Set load timeout in seconds | `.timeoutSeconds(5)` |
43
+ | `.debug()` | Enable debug logging | `.debug()` |
44
+
45
+ ### Loading Strategy Methods
46
+
47
+ | Method | Description | Example |
48
+ |--------|-------------|---------|
49
+ | `.loadingStrategy(Strategy)` | Set how ads are loaded/shown | `.loadingStrategy(AdLoadingStrategy.HYBRID)` |
50
+ | `.waitForLoading()` | Smart wait for splash screens (see below) | `.waitForLoading()` |
51
+
52
+ ### Frequency Control Methods
53
+
54
+ | Method | Description | Example |
55
+ |--------|-------------|---------|
56
+ | `.everyNthTime(Int)` | Show only every Nth call | `.everyNthTime(3)` |
57
+ | `.maxShows(Int)` | Maximum total shows in session | `.maxShows(5)` |
58
+ | `.minInterval(Long)` | Minimum ms between shows | `.minInterval(30000)` |
59
+ | `.minIntervalSeconds(Int)` | Minimum seconds between shows | `.minIntervalSeconds(30)` |
60
+
61
+ ### Callback Methods
62
+
63
+ | Method | Description | Parameters |
64
+ |--------|-------------|------------|
65
+ | `.onAdLoaded(() -> Unit)` | Called when ad loaded successfully | None |
66
+ | `.onAdShown(() -> Unit)` | Called when ad is displayed | None |
67
+ | `.onFailed((LoadAdError) -> Unit)` | Called when loading/showing fails | error: LoadAdError |
68
+
69
+ ### Terminal Methods
70
+
71
+ | Method | Description |
72
+ |--------|-------------|
73
+ | `.show(() -> Unit)` | Load and show ad, then execute callback |
74
+ | `.preload()` | Just preload ad without showing |
75
+
76
+ ---
77
+
78
+ ## 🎯 Loading Strategies
79
+
80
+ ### Available Strategies
81
+
82
+ | Strategy | Behavior | Best For |
83
+ |----------|----------|----------|
84
+ | `ON_DEMAND` | Always fetch fresh ad with loading dialog | Important moments, max revenue |
85
+ | `ONLY_CACHE` | Only show if cached, skip otherwise | Smooth gameplay, no interruption |
86
+ | `HYBRID` | Check cache first, fetch if needed (default) | Balanced UX and fill rate |
87
+
88
+ ### Strategy Examples
89
+
90
+ ```kotlin
91
+ // ON_DEMAND: Always fetch fresh (shows loading dialog)
92
+ InterstitialAdBuilder.with(activity)
93
+ .adUnit(adUnitId)
94
+ .loadingStrategy(AdLoadingStrategy.ON_DEMAND)
95
+ .show { next() }
96
+
97
+ // ONLY_CACHE: Show only if ready, no network request
98
+ InterstitialAdBuilder.with(activity)
99
+ .adUnit(adUnitId)
100
+ .loadingStrategy(AdLoadingStrategy.ONLY_CACHE)
101
+ .show { next() }
102
+
103
+ // HYBRID: Try cache first, fetch with dialog if needed
104
+ InterstitialAdBuilder.with(activity)
105
+ .adUnit(adUnitId)
106
+ .loadingStrategy(AdLoadingStrategy.HYBRID)
107
+ .show { next() }
108
+ ```
109
+
110
+ ---
111
+
112
+ ## 🚀 Splash Screen Pattern
113
+
114
+ Use `.waitForLoading()` for optimal splash screen ad experience:
115
+
116
+ ### How It Works
117
+
118
+ | Ad State | Action |
119
+ |----------|--------|
120
+ | **READY** | Shows immediately |
121
+ | **LOADING** | Waits with timeout, then shows |
122
+ | **NEITHER** | Force fetches with dialog |
123
+
124
+ ### Recommended Pattern
125
+
126
+ ```kotlin
127
+ // Step 1: In Application.onCreate() - Start loading early
128
+ class MyApplication : Application() {
129
+ override fun onCreate() {
130
+ super.onCreate()
131
+ AdManager.getInstance().loadInterstitialAd(this, "ca-app-pub-xxx/yyy")
132
+ }
133
+ }
134
+
135
+ // Step 2: In SplashActivity - Use waitForLoading()
136
+ class SplashActivity : AppCompatActivity() {
137
+
138
+ override fun onCreate(savedInstanceState: Bundle?) {
139
+ super.onCreate(savedInstanceState)
140
+
141
+ // Smart wait: shows cached, waits for loading, or force fetches
142
+ InterstitialAdBuilder.with(this)
143
+ .adUnit("ca-app-pub-xxx/yyy")
144
+ .waitForLoading() // Smart splash behavior
145
+ .timeout(5000) // 5 second max wait
146
+ .show {
147
+ startMainActivity()
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ ### Alternative: Direct AdManager Usage
154
+
155
+ ```kotlin
156
+ // Check states manually
157
+ val adManager = AdManager.getInstance()
158
+
159
+ when {
160
+ adManager.isReady() -> {
161
+ // Ad cached, show immediately
162
+ adManager.forceShowInterstitial(activity, callback)
163
+ }
164
+ adManager.isLoading() -> {
165
+ // Ad loading, use showOrWaitForAd
166
+ adManager.showOrWaitForAd(activity, callback, timeoutMillis = 5000)
167
+ }
168
+ else -> {
169
+ // Nothing happening, force fetch
170
+ adManager.forceShowInterstitialWithDialog(activity, callback)
171
+ }
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ ## 📚 Complete Examples
178
+
179
+ ### Example 1: Basic Navigation
180
+ ```kotlin
181
+ class GameOverActivity : AppCompatActivity() {
182
+
183
+ private fun playAgain() {
184
+ InterstitialAdBuilder.with(this)
185
+ .adUnit(getString(R.string.interstitial_ad_unit))
186
+ .show {
187
+ // Ad shown (or failed), restart game
188
+ startActivity(Intent(this, GameActivity::class.java))
189
+ finish()
190
+ }
191
+ }
192
+ }
193
+ ```
194
+
195
+ ### Example 2: With Fallback Chain
196
+ ```kotlin
197
+ InterstitialAdBuilder.with(this)
198
+ .adUnit("ca-app-pub-xxxxx/primary")
199
+ .fallback("ca-app-pub-xxxxx/backup-1")
200
+ .fallback("ca-app-pub-xxxxx/backup-2")
201
+ .force() // Ignore time interval
202
+ .show {
203
+ goToNextScreen()
204
+ }
205
+ ```
206
+
207
+ ### Example 3: With All Callbacks
208
+ ```kotlin
209
+ InterstitialAdBuilder.with(this)
210
+ .adUnit("ca-app-pub-xxxxx/yyyyy")
211
+ .onAdLoaded {
212
+ Log.d("Ads", "Ad loaded successfully")
213
+ analytics.log("ad_loaded")
214
+ }
215
+ .onAdShown {
216
+ Log.d("Ads", "Ad shown to user")
217
+ analytics.log("ad_shown")
218
+ }
219
+ .onFailed { error ->
220
+ Log.e("Ads", "Ad failed: ${error.message}")
221
+ analytics.log("ad_failed", "error_code" to error.code)
222
+ }
223
+ .show {
224
+ proceedToNextScreen()
225
+ }
226
+ ```
227
+
228
+ ### Example 4: With Loading Dialog
229
+ ```kotlin
230
+ // Show "Please wait..." dialog while ad loads
231
+ InterstitialAdBuilder.with(this)
232
+ .adUnit("ca-app-pub-xxxxx/yyyyy")
233
+ .withLoadingDialog()
234
+ .show {
235
+ continueFlow()
236
+ }
237
+ ```
238
+
239
+ ### Example 5: Count-Limited Ads
240
+ ```kotlin
241
+ // Only show ad first 5 times
242
+ InterstitialAdBuilder.with(this)
243
+ .adUnit("ca-app-pub-xxxxx/yyyyy")
244
+ .maxShows(5) // After 5 displays, skip ad
245
+ .show {
246
+ nextAction()
247
+ }
248
+ ```
249
+
250
+ ### Example 6: Debug Mode
251
+ ```kotlin
252
+ // Enable debug logging for development
253
+ InterstitialAdBuilder.with(this)
254
+ .adUnit("ca-app-pub-xxxxx/yyyyy")
255
+ .debug() // Logs all events
256
+ .show {
257
+ next()
258
+ }
259
+ ```
260
+
261
+ ### Example 7: Preload Pattern
262
+ ```kotlin
263
+ class MainActivity : AppCompatActivity() {
264
+
265
+ override fun onCreate(savedInstanceState: Bundle?) {
266
+ super.onCreate(savedInstanceState)
267
+
268
+ // Preload ad on activity create
269
+ preloadInterstitialAd("ca-app-pub-xxxxx/yyyyy")
270
+ }
271
+
272
+ private fun showAdBeforeExit() {
273
+ // Ad already preloaded, shows instantly
274
+ showInterstitialAd("ca-app-pub-xxxxx/yyyyy") {
275
+ finish()
276
+ }
277
+ }
278
+ }
279
+ ```
280
+
281
+ ### Example 8: Complex Configuration
282
+ ```kotlin
283
+ InterstitialAdBuilder.with(this)
284
+ .adUnit("ca-app-pub-xxxxx/primary")
285
+ .fallbacks("backup-1", "backup-2", "backup-3")
286
+ .force()
287
+ .autoReload(true)
288
+ .withLoadingDialog()
289
+ .onAdLoaded {
290
+ Toast.makeText(this, "Ad ready!", Toast.LENGTH_SHORT).show()
291
+ }
292
+ .onAdShown {
293
+ FirebaseAnalytics.getInstance(this).logEvent("interstitial_shown", null)
294
+ }
295
+ .onFailed { error ->
296
+ Toast.makeText(this, "No ad available", Toast.LENGTH_SHORT).show()
297
+ }
298
+ .debug()
299
+ .show {
300
+ startActivity(Intent(this, NextActivity::class.java))
301
+ finish()
302
+ }
303
+ ```
304
+
305
+ ---
306
+
307
+ ## 🎯 Best Practices
308
+
309
+ ### 1. Always Use Fallbacks
310
+ ```kotlin
311
+ // ✅ GOOD: Multiple ad units for higher fill rate
312
+ InterstitialAdBuilder.with(this)
313
+ .adUnit("primary")
314
+ .fallbacks("backup-1", "backup-2")
315
+ .show { next() }
316
+
317
+ // ❌ BAD: Single ad unit
318
+ InterstitialAdBuilder.with(this)
319
+ .adUnit("primary")
320
+ .show { next() }
321
+ ```
322
+
323
+ ### 2. Preload for Better UX
324
+ ```kotlin
325
+ // ✅ GOOD: Preload early
326
+ class MyActivity : AppCompatActivity() {
327
+ override fun onCreate(savedInstanceState: Bundle?) {
328
+ super.onCreate(savedInstanceState)
329
+ // Preload immediately
330
+ preloadInterstitialAd("ad-unit-id")
331
+ }
332
+
333
+ fun showAd() {
334
+ // Ad already loaded, instant display
335
+ showInterstitialAd("ad-unit-id") { next() }
336
+ }
337
+ }
338
+ ```
339
+
340
+ ### 3. Handle Failures Gracefully
341
+ ```kotlin
342
+ // ✅ GOOD: Always handle errors
343
+ InterstitialAdBuilder.with(this)
344
+ .adUnit("ad-unit")
345
+ .onFailed { error ->
346
+ // Log error but continue
347
+ Log.e("Ads", "Failed: ${error.message}")
348
+ }
349
+ .show {
350
+ // Always proceed regardless of ad result
351
+ continueFlow()
352
+ }
353
+ ```
354
+
355
+ ### 4. Use Debug Mode in Development
356
+ ```kotlin
357
+ // ✅ GOOD: Debug mode for testing
358
+ if (BuildConfig.DEBUG) {
359
+ InterstitialAdBuilder.with(this)
360
+ .adUnit("ad-unit")
361
+ .debug() // See all logs
362
+ .show { next() }
363
+ }
364
+ ```
365
+
366
+ ---
367
+
368
+ ## ⚡ Performance Tips
369
+
370
+ 1. **Preload Early**: Load ads when app starts or on previous screen
371
+ 2. **Use Multiple Fallbacks**: 3-5 ad units for best fill rate
372
+ 3. **Enable Auto-Reload**: Ensures next ad is always ready
373
+ 4. **Respect Intervals**: Don't spam users with too many ads
374
+ 5. **Track Analytics**: Monitor success rates to optimize
375
+
376
+ ---
377
+
378
+ ## 🔍 Comparison: Old vs New API
379
+
380
+ ### Old Approach (Verbose)
381
+ ```kotlin
382
+ val adManager = AdManager.getInstance()
383
+ adManager.loadInterstitialAd(this, "ad-unit",
384
+ object : InterstitialAdLoadCallback() {
385
+ override fun onAdLoaded(ad: InterstitialAd) {
386
+ adManager.forceShowInterstitial(this@Activity,
387
+ object : AdManagerCallback {
388
+ override fun onAdLoaded() {}
389
+ override fun onNextAction() {
390
+ nextScreen()
391
+ }
392
+ override fun onFailedToLoad(error: AdError?) {
393
+ nextScreen()
394
+ }
395
+ })
396
+ }
397
+ override fun onAdFailedToLoad(error: LoadAdError) {
398
+ nextScreen()
399
+ }
400
+ })
401
+ ```
402
+ **Lines: 18** | **Readability: Poor**
403
+
404
+ ### New Approach (Builder)
405
+ ```kotlin
406
+ showInterstitialAd("ad-unit") {
407
+ nextScreen()
408
+ }
409
+ ```
410
+ **Lines: 3** | **Readability: Excellent** ✨
411
+
412
+ ---
413
+
414
+ ## 📊 Feature Matrix
415
+
416
+ | Feature | Old API | Builder API |
417
+ |---------|---------|-------------|
418
+ | Lines of code | 15-20 | **1-5** ✅ |
419
+ | Fallback support | Manual | **Built-in** ✅ |
420
+ | Loading dialog | Separate method | **.withLoadingDialog()** ✅ |
421
+ | Debug mode | N/A | **.debug()** ✅ |
422
+ | Count limiting | Manual tracking | **.maxShows()** ✅ |
423
+ | Callbacks | Nested | **Fluent** ✅ |
424
+ | Type safety | Weak | **Strong** ✅ |
425
+ | Extension functions | No | **Yes** ✅ |
426
+
427
+ ---
428
+
429
+ ## 🎓 Advanced Patterns
430
+
431
+ ### Pattern 1: Context-Aware Ads
432
+ ```kotlin
433
+ fun showContextualAd(activity: Activity, context: String) {
434
+ val adUnit = when (context) {
435
+ "game_over" -> "ca-app-pub-xxx/game-over"
436
+ "level_complete" -> "ca-app-pub-xxx/level-complete"
437
+ else -> "ca-app-pub-xxx/default"
438
+ }
439
+
440
+ InterstitialAdBuilder.with(activity)
441
+ .adUnit(adUnit)
442
+ .show { activity.finish() }
443
+ }
444
+ ```
445
+
446
+ ### Pattern 2: Retry with Different Unit
447
+ ```kotlin
448
+ var retryCount = 0
449
+ fun showAdWithRetry() {
450
+ val units = listOf("primary", "backup-1", "backup-2")
451
+
452
+ InterstitialAdBuilder.with(this)
453
+ .adUnit(units[retryCount])
454
+ .onFailed {
455
+ if (retryCount < units.size - 1) {
456
+ retryCount++
457
+ showAdWithRetry() // Retry with next unit
458
+ }
459
+ }
460
+ .show { next() }
461
+ }
462
+ ```
463
+
464
+ ### Pattern 3: A/B Testing
465
+ ```kotlin
466
+ fun showAdWithABTest() {
467
+ val variant = if (Random.nextBoolean()) "variant-a" else "variant-b"
468
+
469
+ InterstitialAdBuilder.with(this)
470
+ .adUnit("ca-app-pub-xxx/$variant")
471
+ .onAdShown {
472
+ analytics.log("ab_test_shown", "variant" to variant)
473
+ }
474
+ .show { next() }
475
+ }
476
+ ```
477
+
478
+ ---
479
+
480
+ ## 🐛 Troubleshooting
481
+
482
+ ### Ad Not Showing?
483
+ ```kotlin
484
+ // Enable debug mode to see logs
485
+ InterstitialAdBuilder.with(this)
486
+ .adUnit("your-ad-unit")
487
+ .debug() // ← Add this
488
+ .show { next() }
489
+ ```
490
+
491
+ ### Want Loading Indicator?
492
+ ```kotlin
493
+ InterstitialAdBuilder.with(this)
494
+ .adUnit("your-ad-unit")
495
+ .withLoadingDialog() // ← Shows "Please wait..."
496
+ .show { next() }
497
+ ```
498
+
499
+ ### Need Multiple Attempts?
500
+ ```kotlin
501
+ InterstitialAdBuilder.with(this)
502
+ .adUnit("primary")
503
+ .fallback("backup-1") // ← Try this if primary fails
504
+ .fallback("backup-2") // ← Try this if backup-1 fails
505
+ .show { next() }
506
+ ```
507
+
508
+ ---
509
+
510
+ ## 📝 Summary
511
+
512
+ The `InterstitialAdBuilder` makes showing ads:
513
+ - ✅ **Simpler**: 1-3 lines vs 15-20
514
+ - ✅ **Safer**: Type-safe, required validation
515
+ - ✅ **Smarter**: Built-in fallbacks & retry
516
+ - ✅ **Better**: Debug mode, callbacks, customization
517
+
518
+ **Start using it today for cleaner, more maintainable ad code!** 🚀