web3-agent-kit 0.7.0__tar.gz → 0.8.0__tar.gz
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.
- {web3_agent_kit-0.7.0/web3_agent_kit.egg-info → web3_agent_kit-0.8.0}/PKG-INFO +60 -1
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/README.md +59 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/pyproject.toml +1 -1
- web3_agent_kit-0.8.0/src/airdrop/__init__.py +65 -0
- web3_agent_kit-0.8.0/src/airdrop/base.py +268 -0
- web3_agent_kit-0.8.0/src/airdrop/galxe.py +327 -0
- web3_agent_kit-0.8.0/src/airdrop/gleam.py +208 -0
- web3_agent_kit-0.8.0/src/airdrop/multi_wallet.py +264 -0
- web3_agent_kit-0.8.0/src/airdrop/social.py +430 -0
- web3_agent_kit-0.8.0/src/airdrop/tracker.py +339 -0
- web3_agent_kit-0.8.0/src/airdrop/zealy.py +234 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/__init__.py +3 -3
- web3_agent_kit-0.8.0/tests/test_airdrop.py +439 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_api.py +1 -1
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_new_features.py +14 -7
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0/web3_agent_kit.egg-info}/PKG-INFO +60 -1
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/web3_agent_kit.egg-info/SOURCES.txt +9 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/LICENSE +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/setup.cfg +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/setup.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/__init__.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/agent.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/models.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/__init__.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/approval.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/bridge.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/dca.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/gas.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/portfolio.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/swap.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/wallet.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/watcher.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/api/routes/yield_opt.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/approval_manager.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/bridge.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/chain.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/dca_bot.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/defi/__init__.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/gas_optimizer.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/llm.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/multi_wallet.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/plugins/__init__.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/plugins/examples/gas_tracker.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/portfolio.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/sniper.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/utils/__init__.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/wallet.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/wallet_watcher.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/src/yield_optimizer.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_bridge.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_core.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_dca_bot.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_defi.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_llm.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_multi_wallet.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_plugins.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_portfolio.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_sniper.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/tests/test_yield_optimizer.py +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/web3_agent_kit.egg-info/dependency_links.txt +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/web3_agent_kit.egg-info/requires.txt +0 -0
- {web3_agent_kit-0.7.0 → web3_agent_kit-0.8.0}/web3_agent_kit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: web3-agent-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Open-source framework for building autonomous AI agents that interact with blockchain networks
|
|
5
5
|
Author-email: Maulana <khasbim240803@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -68,6 +68,7 @@ Building AI agents that interact with blockchains is **hard**. You need to juggl
|
|
|
68
68
|
| **Security** | Manual approval checks | Auto-scan & revoke risky approvals |
|
|
69
69
|
| **Alerts** | Manual whale tracking | Auto-monitor wallets, instant alerts |
|
|
70
70
|
| **Multi-wallet** | Manage keys manually | Batch ops, consolidated portfolio, wallet groups |
|
|
71
|
+
| **Airdrops** | Manual quest hunting | Auto-track campaigns, multi-wallet farming, Sybil-safe |
|
|
71
72
|
| **Extensibility** | Hard-coded logic | Plugin system — community can extend anything |
|
|
72
73
|
| **Error Handling** | Manual retry logic | Auto-fallback across LLM providers & RPCs |
|
|
73
74
|
|
|
@@ -141,6 +142,7 @@ Building AI agents that interact with blockchains is **hard**. You need to juggl
|
|
|
141
142
|
| **Wallet Watcher** | ✅ | ❌ | ❌ | ❌ |
|
|
142
143
|
| **Yield Optimizer** | ✅ | ❌ | ❌ | ❌ |
|
|
143
144
|
| **Multi-Wallet** | ✅ | ❌ | ❌ | ❌ |
|
|
145
|
+
| **Airdrops** | ✅ | ❌ | ❌ | ❌ |
|
|
144
146
|
| **Plugin System** | ✅ | ❌ | ❌ | ❌ |
|
|
145
147
|
| **Safety Rails** | ✅ Governor | ❌ | ❌ | ❌ |
|
|
146
148
|
| **Natural Language** | ✅ | Partial | ❌ | ❌ |
|
|
@@ -403,6 +405,54 @@ print(summary)
|
|
|
403
405
|
|
|
404
406
|
---
|
|
405
407
|
|
|
408
|
+
## 🪂 Airdrop Automation
|
|
409
|
+
|
|
410
|
+
Discover, track, and complete airdrop campaigns across multiple platforms:
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
from web3_agent_kit.airdrop import (
|
|
414
|
+
GalxePlatform, ZealyPlatform, GleamCampaign,
|
|
415
|
+
AirdropTracker, AirdropFarmer, SocialTaskManager,
|
|
416
|
+
PlatformConfig, TaskType, SybilAvoidanceConfig,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# Track campaigns across platforms
|
|
420
|
+
tracker = AirdropTracker()
|
|
421
|
+
|
|
422
|
+
# Galxe integration
|
|
423
|
+
galxe = GalxePlatform(config=PlatformConfig(api_key="your_key"))
|
|
424
|
+
galxe.login({"api_key": "your_key"})
|
|
425
|
+
tasks = galxe.get_tasks("campaign_id")
|
|
426
|
+
for task in tasks:
|
|
427
|
+
galxe.complete_task(task)
|
|
428
|
+
|
|
429
|
+
# Social task automation
|
|
430
|
+
social = SocialTaskManager()
|
|
431
|
+
social.complete_social_task(TaskType.SOCIAL_TWITTER_FOLLOW, "defi_project")
|
|
432
|
+
social.complete_social_task(TaskType.SOCIAL_DISCORD_JOIN, "https://discord.gg/invite")
|
|
433
|
+
|
|
434
|
+
# Multi-wallet farming with Sybil avoidance
|
|
435
|
+
farmer = AirdropFarmer(
|
|
436
|
+
wallet_manager=manager,
|
|
437
|
+
group="airdrop",
|
|
438
|
+
config=SybilAvoidanceConfig(
|
|
439
|
+
min_delay_between_wallets=60,
|
|
440
|
+
max_delay_between_wallets=600,
|
|
441
|
+
max_tasks_per_wallet_per_day=10,
|
|
442
|
+
),
|
|
443
|
+
)
|
|
444
|
+
results = farmer.farm_campaign(campaign, execute=True)
|
|
445
|
+
|
|
446
|
+
# Export report
|
|
447
|
+
tracker.export_json("./airdrop_report.json")
|
|
448
|
+
tracker.export_csv("./airdrop_report.csv")
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Supported platforms:** Gleam.io, Zealy (Crew3), Galxe (Project Galaxy)
|
|
452
|
+
**Social tasks:** Twitter, Discord, Telegram, YouTube, GitHub
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
406
456
|
## 🌉 Bridge Agent
|
|
407
457
|
|
|
408
458
|
Cross-chain transfers via Li.Fi and Socket:
|
|
@@ -533,6 +583,15 @@ web3-agent-kit/
|
|
|
533
583
|
│ ├── bridge.py # Cross-chain bridge agent
|
|
534
584
|
│ ├── yield_optimizer.py # Yield optimizer + auto-compound
|
|
535
585
|
│ ├── multi_wallet.py # Multi-wallet manager + batch ops
|
|
586
|
+
│ ├── airdrop/ # Airdrop automation module
|
|
587
|
+
│ │ ├── __init__.py # Package exports
|
|
588
|
+
│ │ ├── base.py # Base platform abstraction
|
|
589
|
+
│ │ ├── gleam.py # Gleam.io automation
|
|
590
|
+
│ │ ├── zealy.py # Zealy quest automation
|
|
591
|
+
│ │ ├── galxe.py # Galxe campaign automation
|
|
592
|
+
│ │ ├── social.py # Social task helpers
|
|
593
|
+
│ │ ├── tracker.py # Airdrop tracker
|
|
594
|
+
│ │ └── multi_wallet.py # Multi-wallet farming
|
|
536
595
|
│ ├── plugins/
|
|
537
596
|
│ │ ├── __init__.py # Plugin system (base, registry, manager)
|
|
538
597
|
│ │ └── examples/
|
|
@@ -34,6 +34,7 @@ Building AI agents that interact with blockchains is **hard**. You need to juggl
|
|
|
34
34
|
| **Security** | Manual approval checks | Auto-scan & revoke risky approvals |
|
|
35
35
|
| **Alerts** | Manual whale tracking | Auto-monitor wallets, instant alerts |
|
|
36
36
|
| **Multi-wallet** | Manage keys manually | Batch ops, consolidated portfolio, wallet groups |
|
|
37
|
+
| **Airdrops** | Manual quest hunting | Auto-track campaigns, multi-wallet farming, Sybil-safe |
|
|
37
38
|
| **Extensibility** | Hard-coded logic | Plugin system — community can extend anything |
|
|
38
39
|
| **Error Handling** | Manual retry logic | Auto-fallback across LLM providers & RPCs |
|
|
39
40
|
|
|
@@ -107,6 +108,7 @@ Building AI agents that interact with blockchains is **hard**. You need to juggl
|
|
|
107
108
|
| **Wallet Watcher** | ✅ | ❌ | ❌ | ❌ |
|
|
108
109
|
| **Yield Optimizer** | ✅ | ❌ | ❌ | ❌ |
|
|
109
110
|
| **Multi-Wallet** | ✅ | ❌ | ❌ | ❌ |
|
|
111
|
+
| **Airdrops** | ✅ | ❌ | ❌ | ❌ |
|
|
110
112
|
| **Plugin System** | ✅ | ❌ | ❌ | ❌ |
|
|
111
113
|
| **Safety Rails** | ✅ Governor | ❌ | ❌ | ❌ |
|
|
112
114
|
| **Natural Language** | ✅ | Partial | ❌ | ❌ |
|
|
@@ -369,6 +371,54 @@ print(summary)
|
|
|
369
371
|
|
|
370
372
|
---
|
|
371
373
|
|
|
374
|
+
## 🪂 Airdrop Automation
|
|
375
|
+
|
|
376
|
+
Discover, track, and complete airdrop campaigns across multiple platforms:
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
from web3_agent_kit.airdrop import (
|
|
380
|
+
GalxePlatform, ZealyPlatform, GleamCampaign,
|
|
381
|
+
AirdropTracker, AirdropFarmer, SocialTaskManager,
|
|
382
|
+
PlatformConfig, TaskType, SybilAvoidanceConfig,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Track campaigns across platforms
|
|
386
|
+
tracker = AirdropTracker()
|
|
387
|
+
|
|
388
|
+
# Galxe integration
|
|
389
|
+
galxe = GalxePlatform(config=PlatformConfig(api_key="your_key"))
|
|
390
|
+
galxe.login({"api_key": "your_key"})
|
|
391
|
+
tasks = galxe.get_tasks("campaign_id")
|
|
392
|
+
for task in tasks:
|
|
393
|
+
galxe.complete_task(task)
|
|
394
|
+
|
|
395
|
+
# Social task automation
|
|
396
|
+
social = SocialTaskManager()
|
|
397
|
+
social.complete_social_task(TaskType.SOCIAL_TWITTER_FOLLOW, "defi_project")
|
|
398
|
+
social.complete_social_task(TaskType.SOCIAL_DISCORD_JOIN, "https://discord.gg/invite")
|
|
399
|
+
|
|
400
|
+
# Multi-wallet farming with Sybil avoidance
|
|
401
|
+
farmer = AirdropFarmer(
|
|
402
|
+
wallet_manager=manager,
|
|
403
|
+
group="airdrop",
|
|
404
|
+
config=SybilAvoidanceConfig(
|
|
405
|
+
min_delay_between_wallets=60,
|
|
406
|
+
max_delay_between_wallets=600,
|
|
407
|
+
max_tasks_per_wallet_per_day=10,
|
|
408
|
+
),
|
|
409
|
+
)
|
|
410
|
+
results = farmer.farm_campaign(campaign, execute=True)
|
|
411
|
+
|
|
412
|
+
# Export report
|
|
413
|
+
tracker.export_json("./airdrop_report.json")
|
|
414
|
+
tracker.export_csv("./airdrop_report.csv")
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**Supported platforms:** Gleam.io, Zealy (Crew3), Galxe (Project Galaxy)
|
|
418
|
+
**Social tasks:** Twitter, Discord, Telegram, YouTube, GitHub
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
372
422
|
## 🌉 Bridge Agent
|
|
373
423
|
|
|
374
424
|
Cross-chain transfers via Li.Fi and Socket:
|
|
@@ -499,6 +549,15 @@ web3-agent-kit/
|
|
|
499
549
|
│ ├── bridge.py # Cross-chain bridge agent
|
|
500
550
|
│ ├── yield_optimizer.py # Yield optimizer + auto-compound
|
|
501
551
|
│ ├── multi_wallet.py # Multi-wallet manager + batch ops
|
|
552
|
+
│ ├── airdrop/ # Airdrop automation module
|
|
553
|
+
│ │ ├── __init__.py # Package exports
|
|
554
|
+
│ │ ├── base.py # Base platform abstraction
|
|
555
|
+
│ │ ├── gleam.py # Gleam.io automation
|
|
556
|
+
│ │ ├── zealy.py # Zealy quest automation
|
|
557
|
+
│ │ ├── galxe.py # Galxe campaign automation
|
|
558
|
+
│ │ ├── social.py # Social task helpers
|
|
559
|
+
│ │ ├── tracker.py # Airdrop tracker
|
|
560
|
+
│ │ └── multi_wallet.py # Multi-wallet farming
|
|
502
561
|
│ ├── plugins/
|
|
503
562
|
│ │ ├── __init__.py # Plugin system (base, registry, manager)
|
|
504
563
|
│ │ └── examples/
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "web3-agent-kit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
description = "Open-source framework for building autonomous AI agents that interact with blockchain networks"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Airdrop automation module — discover, track, and complete airdrop campaigns."""
|
|
2
|
+
|
|
3
|
+
from .base import (
|
|
4
|
+
BaseAirdropPlatform,
|
|
5
|
+
PlatformConfig,
|
|
6
|
+
AirdropTask,
|
|
7
|
+
AirdropCampaign,
|
|
8
|
+
TaskType,
|
|
9
|
+
TaskStatus,
|
|
10
|
+
)
|
|
11
|
+
from .gleam import GleamCampaign
|
|
12
|
+
from .zealy import ZealyPlatform, ZealyQuest, ZealyLeaderboardEntry
|
|
13
|
+
from .galxe import GalxePlatform, GalxeCredential, GalxePoints
|
|
14
|
+
from .social import (
|
|
15
|
+
SocialTaskManager,
|
|
16
|
+
SocialAccount,
|
|
17
|
+
SocialPlatform,
|
|
18
|
+
SocialTaskResult,
|
|
19
|
+
TwitterHelper,
|
|
20
|
+
DiscordHelper,
|
|
21
|
+
TelegramHelper,
|
|
22
|
+
YouTubeHelper,
|
|
23
|
+
GitHubHelper,
|
|
24
|
+
)
|
|
25
|
+
from .tracker import AirdropTracker, AirdropReward, AirdropSummary
|
|
26
|
+
from .multi_wallet import AirdropFarmer, SybilAvoidanceConfig, FarmResult, WalletFarmProgress
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Base
|
|
30
|
+
"BaseAirdropPlatform",
|
|
31
|
+
"PlatformConfig",
|
|
32
|
+
"AirdropTask",
|
|
33
|
+
"AirdropCampaign",
|
|
34
|
+
"TaskType",
|
|
35
|
+
"TaskStatus",
|
|
36
|
+
# Gleam
|
|
37
|
+
"GleamCampaign",
|
|
38
|
+
# Zealy
|
|
39
|
+
"ZealyPlatform",
|
|
40
|
+
"ZealyQuest",
|
|
41
|
+
"ZealyLeaderboardEntry",
|
|
42
|
+
# Galxe
|
|
43
|
+
"GalxePlatform",
|
|
44
|
+
"GalxeCredential",
|
|
45
|
+
"GalxePoints",
|
|
46
|
+
# Social
|
|
47
|
+
"SocialTaskManager",
|
|
48
|
+
"SocialAccount",
|
|
49
|
+
"SocialPlatform",
|
|
50
|
+
"SocialTaskResult",
|
|
51
|
+
"TwitterHelper",
|
|
52
|
+
"DiscordHelper",
|
|
53
|
+
"TelegramHelper",
|
|
54
|
+
"YouTubeHelper",
|
|
55
|
+
"GitHubHelper",
|
|
56
|
+
# Tracker
|
|
57
|
+
"AirdropTracker",
|
|
58
|
+
"AirdropReward",
|
|
59
|
+
"AirdropSummary",
|
|
60
|
+
# Multi-wallet farming
|
|
61
|
+
"AirdropFarmer",
|
|
62
|
+
"SybilAvoidanceConfig",
|
|
63
|
+
"FarmResult",
|
|
64
|
+
"WalletFarmProgress",
|
|
65
|
+
]
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""Base airdrop platform abstraction — common interface for all platforms."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TaskType(Enum):
|
|
18
|
+
"""Types of airdrop tasks."""
|
|
19
|
+
SOCIAL_TWITTER_FOLLOW = "twitter_follow"
|
|
20
|
+
SOCIAL_TWITTER_RETWEET = "twitter_retweet"
|
|
21
|
+
SOCIAL_TWITTER_LIKE = "twitter_like"
|
|
22
|
+
SOCIAL_TWITTER_COMMENT = "twitter_comment"
|
|
23
|
+
SOCIAL_DISCORD_JOIN = "discord_join"
|
|
24
|
+
SOCIAL_DISCORD_VERIFY = "discord_verify"
|
|
25
|
+
SOCIAL_TELEGRAM_JOIN = "telegram_join"
|
|
26
|
+
SOCIAL_YOUTUBE_SUBSCRIBE = "youtube_subscribe"
|
|
27
|
+
SOCIAL_GITHUB_STAR = "github_star"
|
|
28
|
+
SOCIAL_GITHUB_FORK = "github_fork"
|
|
29
|
+
ON_CHAIN_TX = "on_chain_tx"
|
|
30
|
+
ON_CHAIN_SWAP = "on_chain_swap"
|
|
31
|
+
ON_CHAIN_BRIDGE = "on_chain_bridge"
|
|
32
|
+
ON_CHAIN_STAKE = "on_chain_stake"
|
|
33
|
+
QUIZ = "quiz"
|
|
34
|
+
VISIT_URL = "visit_url"
|
|
35
|
+
WALLET_CONNECT = "wallet_connect"
|
|
36
|
+
REFERRAL = "referral"
|
|
37
|
+
CUSTOM = "custom"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TaskStatus(Enum):
|
|
41
|
+
"""Status of a task."""
|
|
42
|
+
PENDING = "pending"
|
|
43
|
+
IN_PROGRESS = "in_progress"
|
|
44
|
+
COMPLETED = "completed"
|
|
45
|
+
VERIFIED = "verified"
|
|
46
|
+
FAILED = "failed"
|
|
47
|
+
SKIPPED = "skipped"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class AirdropTask:
|
|
52
|
+
"""A single airdrop task."""
|
|
53
|
+
task_id: str
|
|
54
|
+
platform: str
|
|
55
|
+
task_type: TaskType
|
|
56
|
+
title: str
|
|
57
|
+
description: str = ""
|
|
58
|
+
url: str = ""
|
|
59
|
+
points: float = 0
|
|
60
|
+
status: TaskStatus = TaskStatus.PENDING
|
|
61
|
+
completed_at: Optional[float] = None
|
|
62
|
+
metadata: dict = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def is_social(self) -> bool:
|
|
66
|
+
return self.task_type.name.startswith("SOCIAL_")
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def is_on_chain(self) -> bool:
|
|
70
|
+
return self.task_type.name.startswith("ON_CHAIN_")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class AirdropCampaign:
|
|
75
|
+
"""An airdrop campaign listing."""
|
|
76
|
+
campaign_id: str
|
|
77
|
+
platform: str
|
|
78
|
+
name: str
|
|
79
|
+
description: str = ""
|
|
80
|
+
url: str = ""
|
|
81
|
+
total_points: float = 0
|
|
82
|
+
earned_points: float = 0
|
|
83
|
+
deadline: Optional[float] = None
|
|
84
|
+
tasks: list[AirdropTask] = field(default_factory=list)
|
|
85
|
+
is_active: bool = True
|
|
86
|
+
metadata: dict = field(default_factory=dict)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def progress(self) -> float:
|
|
90
|
+
if self.total_points == 0:
|
|
91
|
+
return 0.0
|
|
92
|
+
return self.earned_points / self.total_points
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def is_expired(self) -> bool:
|
|
96
|
+
if self.deadline is None:
|
|
97
|
+
return False
|
|
98
|
+
return time.time() > self.deadline
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class PlatformConfig:
|
|
103
|
+
"""Configuration for an airdrop platform."""
|
|
104
|
+
api_key: Optional[str] = None
|
|
105
|
+
session_cookie: Optional[str] = None
|
|
106
|
+
user_agent: str = (
|
|
107
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
108
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
109
|
+
"Chrome/120.0.0.0 Safari/537.36"
|
|
110
|
+
)
|
|
111
|
+
rate_limit_delay: float = 2.0
|
|
112
|
+
max_retries: int = 3
|
|
113
|
+
retry_delay: float = 5.0
|
|
114
|
+
timeout: int = 30
|
|
115
|
+
proxy: Optional[str] = None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class BaseAirdropPlatform(ABC):
|
|
119
|
+
"""Abstract base class for all airdrop platform integrations.
|
|
120
|
+
|
|
121
|
+
Provides common functionality: session management, rate limiting,
|
|
122
|
+
retry logic, and the standard interface every platform must implement.
|
|
123
|
+
|
|
124
|
+
Example::
|
|
125
|
+
|
|
126
|
+
class MyPlatform(BaseAirdropPlatform):
|
|
127
|
+
platform_name = "my_platform"
|
|
128
|
+
|
|
129
|
+
def login(self, credentials):
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
def get_tasks(self, campaign_id):
|
|
133
|
+
...
|
|
134
|
+
|
|
135
|
+
def complete_task(self, task):
|
|
136
|
+
...
|
|
137
|
+
|
|
138
|
+
def verify_completion(self, task):
|
|
139
|
+
...
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
platform_name: str = "unknown"
|
|
143
|
+
|
|
144
|
+
def __init__(self, config: Optional[PlatformConfig] = None):
|
|
145
|
+
self.config = config or PlatformConfig()
|
|
146
|
+
self.session = requests.Session()
|
|
147
|
+
self.session.headers.update({
|
|
148
|
+
"User-Agent": self.config.user_agent,
|
|
149
|
+
})
|
|
150
|
+
if self.config.session_cookie:
|
|
151
|
+
self.session.cookies.set("session", self.config.session_cookie)
|
|
152
|
+
if self.config.proxy:
|
|
153
|
+
self.session.proxies = {"http": self.config.proxy, "https": self.config.proxy}
|
|
154
|
+
self._last_request_time: float = 0
|
|
155
|
+
self._authenticated: bool = False
|
|
156
|
+
logger.info(f"Initialized {self.platform_name} platform")
|
|
157
|
+
|
|
158
|
+
@abstractmethod
|
|
159
|
+
def login(self, credentials: dict) -> bool:
|
|
160
|
+
"""Authenticate with the platform.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
credentials: Platform-specific credentials.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
True if login succeeded.
|
|
167
|
+
"""
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
@abstractmethod
|
|
171
|
+
def get_tasks(self, campaign_id: str) -> list[AirdropTask]:
|
|
172
|
+
"""Get available tasks for a campaign.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
campaign_id: Platform-specific campaign identifier.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
List of AirdropTask objects.
|
|
179
|
+
"""
|
|
180
|
+
...
|
|
181
|
+
|
|
182
|
+
@abstractmethod
|
|
183
|
+
def complete_task(self, task: AirdropTask) -> bool:
|
|
184
|
+
"""Attempt to complete a task.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
task: The task to complete.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
True if task was completed successfully.
|
|
191
|
+
"""
|
|
192
|
+
...
|
|
193
|
+
|
|
194
|
+
@abstractmethod
|
|
195
|
+
def verify_completion(self, task: AirdropTask) -> bool:
|
|
196
|
+
"""Verify that a task is completed.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
task: The task to verify.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
True if verified as completed.
|
|
203
|
+
"""
|
|
204
|
+
...
|
|
205
|
+
|
|
206
|
+
def discover_campaigns(self) -> list[AirdropCampaign]:
|
|
207
|
+
"""Discover available campaigns. Override in subclasses.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
List of AirdropCampaign objects.
|
|
211
|
+
"""
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
def _rate_limit(self):
|
|
215
|
+
"""Enforce rate limiting between requests."""
|
|
216
|
+
elapsed = time.time() - self._last_request_time
|
|
217
|
+
if elapsed < self.config.rate_limit_delay:
|
|
218
|
+
sleep_time = self.config.rate_limit_delay - elapsed
|
|
219
|
+
logger.debug(f"Rate limiting: sleeping {sleep_time:.1f}s")
|
|
220
|
+
time.sleep(sleep_time)
|
|
221
|
+
self._last_request_time = time.time()
|
|
222
|
+
|
|
223
|
+
def _request(
|
|
224
|
+
self,
|
|
225
|
+
method: str,
|
|
226
|
+
url: str,
|
|
227
|
+
**kwargs,
|
|
228
|
+
) -> requests.Response:
|
|
229
|
+
"""Make an HTTP request with rate limiting and retry logic.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
method: HTTP method (GET, POST, etc.).
|
|
233
|
+
url: Request URL.
|
|
234
|
+
**kwargs: Passed to requests.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Response object.
|
|
238
|
+
|
|
239
|
+
Raises:
|
|
240
|
+
requests.RequestException: After all retries exhausted.
|
|
241
|
+
"""
|
|
242
|
+
kwargs.setdefault("timeout", self.config.timeout)
|
|
243
|
+
last_error = None
|
|
244
|
+
|
|
245
|
+
for attempt in range(self.config.max_retries):
|
|
246
|
+
self._rate_limit()
|
|
247
|
+
try:
|
|
248
|
+
response = self.session.request(method, url, **kwargs)
|
|
249
|
+
if response.status_code == 429:
|
|
250
|
+
retry_after = int(response.headers.get("Retry-After", self.config.retry_delay))
|
|
251
|
+
logger.warning(f"Rate limited, waiting {retry_after}s")
|
|
252
|
+
time.sleep(retry_after)
|
|
253
|
+
continue
|
|
254
|
+
response.raise_for_status()
|
|
255
|
+
return response
|
|
256
|
+
except requests.RequestException as e:
|
|
257
|
+
last_error = e
|
|
258
|
+
logger.warning(f"Request failed (attempt {attempt + 1}): {e}")
|
|
259
|
+
if attempt < self.config.max_retries - 1:
|
|
260
|
+
time.sleep(self.config.retry_delay * (attempt + 1))
|
|
261
|
+
|
|
262
|
+
raise last_error # type: ignore[misc]
|
|
263
|
+
|
|
264
|
+
def _get(self, url: str, **kwargs) -> requests.Response:
|
|
265
|
+
return self._request("GET", url, **kwargs)
|
|
266
|
+
|
|
267
|
+
def _post(self, url: str, **kwargs) -> requests.Response:
|
|
268
|
+
return self._request("POST", url, **kwargs)
|