From 480a0bc8a910cd1e1e9cc528ab761f26e62715ce Mon Sep 17 00:00:00 2001 From: Jaammerr Date: Tue, 23 Jul 2024 19:40:06 +0300 Subject: [PATCH] Global Update V3 --- README.md | 64 +- abi/cometa.json | 1 + abi/commemorative_nft.json | 1141 +++++++++++ abi/green_id.json | 775 ++++++++ abi/make_nft_great_again.json | 752 ++++++++ abi/mint_air3.json | 1079 +++++++++++ abi/mint_flag.json | 1141 +++++++++++ abi/mint_shop.json | 1079 +++++++++++ abi/mint_supermint.json | 1079 +++++++++++ abi/omnihub.json | 250 +++ abi/summer_nft.json | 54 + abi/vip3_nft.json | 862 +++++++++ config/__init__.py | 1 - config/accounts.txt | 5 +- config/settings.yaml | 35 +- console/images/console.png | Bin 124212 -> 39484 bytes console/main.py | 44 +- core/api.py | 574 ++++++ core/bot.py | 549 ++++++ core/exceptions/base.py | 10 + core/modules/__init__.py | 3 + core/modules/comet_bridge.py | 75 + core/modules/createx_api.py | 239 +++ core/modules/vip3_api.py | 128 ++ core/wallet.py | 349 ++++ loader.py | 2 +- models/account.py | 1 - models/api.py | 25 +- models/config.py | 13 +- models/onchain.py | 67 +- requirements.txt | 18 +- run.py | 57 +- src/api.py | 334 ---- src/bot.py | 166 -- src/bridge.py | 72 - src/exceptions/base.py | 4 - src/wallet.py | 31 - twitter_api/__init__.py | 2 - twitter_api/account.py | 1664 ----------------- twitter_api/constants.py | 801 -------- twitter_api/errors.py | 88 - twitter_api/models/__init__.py | 3 - twitter_api/models/data/__init__.py | 2 - twitter_api/models/data/bind_account_v1.py | 11 - twitter_api/models/data/bind_account_v2.py | 20 - twitter_api/models/tweets/__init__.py | 14 - twitter_api/models/tweets/bookmark_tweet.py | 14 - twitter_api/models/tweets/create_reply.py | 150 -- .../models/tweets/create_schedule_tweet.py | 22 - twitter_api/models/tweets/create_tweet.py | 156 -- .../models/tweets/delete_favorite_tweet.py | 14 - twitter_api/models/tweets/delete_retweet.py | 31 - twitter_api/models/tweets/delete_tweet.py | 21 - .../models/tweets/delete_unschedule_tweet.py | 15 - twitter_api/models/tweets/favorite_tweet.py | 14 - twitter_api/models/tweets/retweet.py | 32 - twitter_api/models/tweets/scrape_favorites.py | 32 - twitter_api/models/tweets/scrape_replies.py | 37 - twitter_api/models/tweets/scrape_retweets.py | 20 - twitter_api/models/tweets/unbookmark_tweet.py | 14 - twitter_api/models/users/__init__.py | 3 - twitter_api/models/users/followers.py | 16 - twitter_api/models/users/follows.py | 70 - twitter_api/models/users/user_info.py | 66 - twitter_api/requirements.txt | 5 - twitter_api/util.py | 296 --- utils/__init__.py | 4 +- utils/{main.py => console.py} | 4 +- utils/file_utils.py | 12 + {config => utils}/load_config.py | 29 +- 70 files changed, 10419 insertions(+), 4342 deletions(-) create mode 100644 abi/cometa.json create mode 100644 abi/commemorative_nft.json create mode 100644 abi/green_id.json create mode 100644 abi/make_nft_great_again.json create mode 100644 abi/mint_air3.json create mode 100644 abi/mint_flag.json create mode 100644 abi/mint_shop.json create mode 100644 abi/mint_supermint.json create mode 100644 abi/omnihub.json create mode 100644 abi/summer_nft.json create mode 100644 abi/vip3_nft.json delete mode 100644 config/__init__.py create mode 100644 core/api.py create mode 100644 core/bot.py create mode 100644 core/exceptions/base.py create mode 100644 core/modules/__init__.py create mode 100644 core/modules/comet_bridge.py create mode 100644 core/modules/createx_api.py create mode 100644 core/modules/vip3_api.py create mode 100644 core/wallet.py delete mode 100644 src/api.py delete mode 100644 src/bot.py delete mode 100644 src/bridge.py delete mode 100644 src/exceptions/base.py delete mode 100644 src/wallet.py delete mode 100644 twitter_api/__init__.py delete mode 100644 twitter_api/account.py delete mode 100644 twitter_api/constants.py delete mode 100644 twitter_api/errors.py delete mode 100644 twitter_api/models/__init__.py delete mode 100644 twitter_api/models/data/__init__.py delete mode 100644 twitter_api/models/data/bind_account_v1.py delete mode 100644 twitter_api/models/data/bind_account_v2.py delete mode 100644 twitter_api/models/tweets/__init__.py delete mode 100644 twitter_api/models/tweets/bookmark_tweet.py delete mode 100644 twitter_api/models/tweets/create_reply.py delete mode 100644 twitter_api/models/tweets/create_schedule_tweet.py delete mode 100644 twitter_api/models/tweets/create_tweet.py delete mode 100644 twitter_api/models/tweets/delete_favorite_tweet.py delete mode 100644 twitter_api/models/tweets/delete_retweet.py delete mode 100644 twitter_api/models/tweets/delete_tweet.py delete mode 100644 twitter_api/models/tweets/delete_unschedule_tweet.py delete mode 100644 twitter_api/models/tweets/favorite_tweet.py delete mode 100644 twitter_api/models/tweets/retweet.py delete mode 100644 twitter_api/models/tweets/scrape_favorites.py delete mode 100644 twitter_api/models/tweets/scrape_replies.py delete mode 100644 twitter_api/models/tweets/scrape_retweets.py delete mode 100644 twitter_api/models/tweets/unbookmark_tweet.py delete mode 100644 twitter_api/models/users/__init__.py delete mode 100644 twitter_api/models/users/followers.py delete mode 100644 twitter_api/models/users/follows.py delete mode 100644 twitter_api/models/users/user_info.py delete mode 100644 twitter_api/requirements.txt delete mode 100644 twitter_api/util.py rename utils/{main.py => console.py} (78%) create mode 100644 utils/file_utils.py rename {config => utils}/load_config.py (66%) diff --git a/README.md b/README.md index d3c6189..2f81b97 100644 --- a/README.md +++ b/README.md @@ -17,22 +17,28 @@ ## 🤖 | Features: - **Auto registration** -- **Auto bind referral** -- **Auto bind twitter** -- **Auto collect all possible rewards (boxes, energy)** -- **Auto spin turntable** -- **Auto inject** -- **Auto bridge from SEPOLIA to MINT (testnet)** -- **Auto completing tasks** - - -## 📝 | Description: -```Auto bridge from SEPOLIA to MINT (testnet)``` -```- The script will bridge a random amount of ETH from SEPOLIA to MINT (testnet).``` - -```Auto completing tasks``` -```- The script will complete all twitter tasks and testnet``` - +- **Bind referral** +- **Bind twitter** +- **Collect all possible rewards (boxes, energy)** +- **Spin turntable** +- **Inject** +- **Completing tasks** +- **Bridge from ARB to MINT via CometBridge** +- **Fix sign** +- **Export Trees IDs** +- **Mint the following NFTs:** + + - Green ID + - Commemorative NFT + - Make NFT Great Again + - Flag NFT + - Shop NFT + - Air3 NFT + - SuperMint NFT + - Owlto SummerFest NFT + - Omnihub SummerFest NFT + - Vip3 NFT + - Summer NFT @@ -50,18 +56,22 @@ ## ⚙️ Config (config > settings.yaml) -| Name | Description | -|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------| -| referral_code | Your referral code | -| eth_rpc_url | ETH RPC URL (if not have, leave the default value) | -| sepolia_rpc_url | SEPOLIA RPC URL (if not have, leave the default value) | -| threads | Number of accounts that will work simultaneously | -| min_delay_before_start | min delay before start accounts actions (in seconds) | -| max_delay_before_start | max delay before start accounts actions (in seconds) | -| min_amount_to_bridge | min amount of ETH to bridge from SEPOLIA to MINT | -| max_amount_to_bridge | max amount of ETH to bridge from SEPOLIA to MINT | +| Name | Description | +|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| referral_code | Your referral code | +| mint_rpc_url | MINT RPC URL (if not have, leave the default value) | +| arb_rpc_url | ARB RPC URL (if not have, leave the default value) | +| threads | Number of accounts that will work simultaneously | +| min_delay_before_start | min delay before start accounts actions (in seconds) | +| max_delay_before_start | max delay before start accounts actions (in seconds) | | spin_turntable_by_percentage_of_energy | percentage of balance that will be spent on spins (for example, if you have 500 energy daily and you bet 60%, the script will make 1 spin on your account) | -| shuffle_accounts | shuffle accounts before start | +| shuffle_accounts | shuffle accounts before start | +| mint_random_all_nfts | mint random NFTs in list | +delay_between_mint_min | min delay between mint NFTs (in seconds) | +| delay_between_mint_max | max delay between mint NFTs (in seconds) | +| comet_bridge_wallet | main wallet (pr or mnemonic) for bridge from ARB to MINT multi wallets | +| comet_bridge_amount_min | min amount for bridge from ARB to MINT | +| comet_bridge_amount_max | max amount for bridge from ARB to MINT | ## ⚙️ Accounts format (config > accounts.txt) diff --git a/abi/cometa.json b/abi/cometa.json new file mode 100644 index 0000000..fe3cfb9 --- /dev/null +++ b/abi/cometa.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AmountMustEqualValue","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"Initialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidArgs","type":"error"},{"inputs":[],"name":"InvalidProvider","type":"error"},{"inputs":[],"name":"InvalidRecipientAddress","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[],"name":"OnlyEnabled","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ParamsError","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"Bridged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isEnabled","type":"bool"}],"name":"EnableChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"Released","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address payable","name":"provider","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"bridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isEnabled","type":"bool"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"address payable[]","name":"recipients","type":"address[]"},{"internalType":"bytes[]","name":"metadatas","type":"bytes[]"}],"name":"multiRelease","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address payable","name":"recipient","type":"address"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"name":"release","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isEnabled","type":"bool"}],"name":"setIsEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/abi/commemorative_nft.json b/abi/commemorative_nft.json new file mode 100644 index 0000000..f6049f2 --- /dev/null +++ b/abi/commemorative_nft.json @@ -0,0 +1,1141 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_nftName", + "type": "string" + }, + { + "internalType": "string", + "name": "_nftToken", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxPerWallet", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ApprovalQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintERC2309QuantityExceedsLimit", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "OwnerQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipNotInitializedForExtraData", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwner", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC721ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "URIQueryForNonexistentToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "ref", + "type": "address" + } + ], + "name": "FundsDistributed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "ref", + "type": "address" + } + ], + "name": "MintWithRef", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "OWNER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "_addressData", + "outputs": [ + { + "internalType": "uint256", + "name": "WalletMinted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "WithdrawStatus", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEndless", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkMaxPerWallet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkOnlyCorePayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkTotalPayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkUnlimitedSupply", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "coreTeam", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_mintTo", + "type": "address" + } + ], + "name": "devMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxPerWallet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "mintEndDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPause", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintStartDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutRefPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxperwallet", + "type": "uint256" + } + ], + "name": "setMaxPerWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + } + ], + "name": "setMaxSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + } + ], + "name": "setMintPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + } + ], + "name": "setNewEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + } + ], + "name": "setNewStartDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_pauseStatus", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_address", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_percent", + "type": "uint256[]" + } + ], + "name": "setPayoutCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ownerAddress", + "type": "address" + } + ], + "name": "setPayoutOwnerAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutOwnerPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutRefPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenMinted", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/abi/green_id.json b/abi/green_id.json new file mode 100644 index 0000000..44a6e8d --- /dev/null +++ b/abi/green_id.json @@ -0,0 +1,775 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC721IncorrectOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC721InsufficientApproval", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC721InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "ERC721InvalidOperator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC721InvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC721InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC721InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC721NonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "UUPSUnauthorizedCallContext", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "UUPSUnsupportedProxiableUUID", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "TokenClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "UPGRADE_INTERFACE_VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "claimedTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "internalType": "struct GreenID.MintParam[]", + "name": "params", + "type": "tuple[]" + } + ], + "name": "mintBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "uri", + "type": "string" + } + ], + "name": "setDefaultURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "uri", + "type": "string" + } + ], + "name": "setDynamicURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/abi/make_nft_great_again.json b/abi/make_nft_great_again.json new file mode 100644 index 0000000..ce0454d --- /dev/null +++ b/abi/make_nft_great_again.json @@ -0,0 +1,752 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_totalsupply", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "string", + "name": "__base_uri", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC721IncorrectOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC721InsufficientApproval", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC721InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "ERC721InvalidOperator", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ERC721InvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC721InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC721InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ERC721NonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "ExceedingTotalSupply", + "type": "error" + }, + { + "inputs": [], + "name": "HasClaimed", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidMerkleRoot", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidProof", + "type": "error" + }, + { + "inputs": [], + "name": "IsStop", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroRoot", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_toTokenId", + "type": "uint256" + } + ], + "name": "BatchMetadataUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "MetadataUpdate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "_baseUri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "proofs", + "type": "bytes32[]" + } + ], + "name": "awardItem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAvailableCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isClaimed", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isStop", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "merkleRoot", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextTokenId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "baseUri", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_isStop", + "type": "bool" + } + ], + "name": "setStop", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_tree", + "type": "bytes32" + } + ], + "name": "updateTree", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/abi/mint_air3.json b/abi/mint_air3.json new file mode 100644 index 0000000..5861071 --- /dev/null +++ b/abi/mint_air3.json @@ -0,0 +1,1079 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_nftName", + "type": "string" + }, + { + "internalType": "string", + "name": "_nftToken", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxPerWallet", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ApprovalQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintERC2309QuantityExceedsLimit", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "OwnerQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipNotInitializedForExtraData", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwner", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC721ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "URIQueryForNonexistentToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "OWNER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "_addressData", + "outputs": [ + { + "internalType": "uint256", + "name": "WalletMinted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "WithdrawStatus", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEndless", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkMaxPerWallet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkOnlyCorePayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkTotalPayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkUnlimitedSupply", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "coreTeam", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_mintTo", + "type": "address" + } + ], + "name": "devMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxPerWallet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "mintEndDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPause", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintStartDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutRefPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxperwallet", + "type": "uint256" + } + ], + "name": "setMaxPerWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + } + ], + "name": "setMaxSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + } + ], + "name": "setMintPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + } + ], + "name": "setNewEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + } + ], + "name": "setNewStartDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_pauseStatus", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_address", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_percent", + "type": "uint256[]" + } + ], + "name": "setPayoutCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ownerAddress", + "type": "address" + } + ], + "name": "setPayoutOwnerAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutOwnerPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutRefPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenMinted", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_cost", + "type": "uint256" + } + ], + "name": "transferETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/abi/mint_flag.json b/abi/mint_flag.json new file mode 100644 index 0000000..f6049f2 --- /dev/null +++ b/abi/mint_flag.json @@ -0,0 +1,1141 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_nftName", + "type": "string" + }, + { + "internalType": "string", + "name": "_nftToken", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxPerWallet", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ApprovalQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintERC2309QuantityExceedsLimit", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "OwnerQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipNotInitializedForExtraData", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwner", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC721ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "URIQueryForNonexistentToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "ref", + "type": "address" + } + ], + "name": "FundsDistributed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "ref", + "type": "address" + } + ], + "name": "MintWithRef", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "OWNER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "_addressData", + "outputs": [ + { + "internalType": "uint256", + "name": "WalletMinted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "WithdrawStatus", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEndless", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkMaxPerWallet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkOnlyCorePayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkTotalPayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkUnlimitedSupply", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "coreTeam", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_mintTo", + "type": "address" + } + ], + "name": "devMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxPerWallet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "mintEndDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPause", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintStartDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutRefPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxperwallet", + "type": "uint256" + } + ], + "name": "setMaxPerWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + } + ], + "name": "setMaxSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + } + ], + "name": "setMintPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + } + ], + "name": "setNewEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + } + ], + "name": "setNewStartDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_pauseStatus", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_address", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_percent", + "type": "uint256[]" + } + ], + "name": "setPayoutCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ownerAddress", + "type": "address" + } + ], + "name": "setPayoutOwnerAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutOwnerPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutRefPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenMinted", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/abi/mint_shop.json b/abi/mint_shop.json new file mode 100644 index 0000000..5861071 --- /dev/null +++ b/abi/mint_shop.json @@ -0,0 +1,1079 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_nftName", + "type": "string" + }, + { + "internalType": "string", + "name": "_nftToken", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxPerWallet", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ApprovalQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintERC2309QuantityExceedsLimit", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "OwnerQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipNotInitializedForExtraData", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwner", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC721ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "URIQueryForNonexistentToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "OWNER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "_addressData", + "outputs": [ + { + "internalType": "uint256", + "name": "WalletMinted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "WithdrawStatus", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEndless", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkMaxPerWallet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkOnlyCorePayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkTotalPayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkUnlimitedSupply", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "coreTeam", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_mintTo", + "type": "address" + } + ], + "name": "devMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxPerWallet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "mintEndDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPause", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintStartDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutRefPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxperwallet", + "type": "uint256" + } + ], + "name": "setMaxPerWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + } + ], + "name": "setMaxSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + } + ], + "name": "setMintPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + } + ], + "name": "setNewEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + } + ], + "name": "setNewStartDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_pauseStatus", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_address", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_percent", + "type": "uint256[]" + } + ], + "name": "setPayoutCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ownerAddress", + "type": "address" + } + ], + "name": "setPayoutOwnerAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutOwnerPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutRefPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenMinted", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_cost", + "type": "uint256" + } + ], + "name": "transferETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/abi/mint_supermint.json b/abi/mint_supermint.json new file mode 100644 index 0000000..5861071 --- /dev/null +++ b/abi/mint_supermint.json @@ -0,0 +1,1079 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "_nftName", + "type": "string" + }, + { + "internalType": "string", + "name": "_nftToken", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxPerWallet", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "ApprovalQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceQueryForZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintERC2309QuantityExceedsLimit", + "type": "error" + }, + { + "inputs": [], + "name": "MintToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "MintZeroQuantity", + "type": "error" + }, + { + "inputs": [], + "name": "OwnerQueryForNonexistentToken", + "type": "error" + }, + { + "inputs": [], + "name": "OwnershipNotInitializedForExtraData", + "type": "error" + }, + { + "inputs": [], + "name": "TransferCallerNotOwnerNorApproved", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFromIncorrectOwner", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToNonERC721ReceiverImplementer", + "type": "error" + }, + { + "inputs": [], + "name": "TransferToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "URIQueryForNonexistentToken", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "ConsecutiveTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "OWNER", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "_addressData", + "outputs": [ + { + "internalType": "uint256", + "name": "WalletMinted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "WithdrawStatus", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_payoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkEndless", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkMaxPerWallet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkOnlyCorePayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkTotalPayoutPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "checkUnlimitedSupply", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "coreTeam", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_mintTo", + "type": "address" + } + ], + "name": "devMint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxPerWallet", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintAmount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "mintEndDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPause", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintStartDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutCoreTeamPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "payoutFinalPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutOwnerPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "payoutRefPercent", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newBaseURI", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxperwallet", + "type": "uint256" + } + ], + "name": "setMaxPerWallet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxSupply", + "type": "uint256" + } + ], + "name": "setMaxSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintPrice", + "type": "uint256" + } + ], + "name": "setMintPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintEndDate", + "type": "uint256" + } + ], + "name": "setNewEndDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintStartDate", + "type": "uint256" + } + ], + "name": "setNewStartDate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_pauseStatus", + "type": "bool" + } + ], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_address", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_percent", + "type": "uint256[]" + } + ], + "name": "setPayoutCoreTeam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ownerAddress", + "type": "address" + } + ], + "name": "setPayoutOwnerAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutOwnerPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_percent", + "type": "uint256" + } + ], + "name": "setPayoutRefPercent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenMinted", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "internalType": "address", + "name": "_ref", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_cost", + "type": "uint256" + } + ], + "name": "transferETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/abi/omnihub.json b/abi/omnihub.json new file mode 100644 index 0000000..0537010 --- /dev/null +++ b/abi/omnihub.json @@ -0,0 +1,250 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deployProtocolFee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_mintProtocolFee", + "type": "uint256" + }, + { + "internalType": "string", + "name": "_contractBaseURI", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "deployedContractAddress", + "type": "address" + } + ], + "name": "ContractDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "contractBaseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_price", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_supply", + "type": "uint256" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "contract ERC721OmniHubContract", + "name": "", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "deployProtocolFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mintProtocolFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_contractBaseURI", + "type": "string" + } + ], + "name": "setContractBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_deployProtocolFee", + "type": "uint256" + } + ], + "name": "setDeployProtocolFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_mintProtocolFee", + "type": "uint256" + } + ], + "name": "setMintProtocolFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/abi/summer_nft.json b/abi/summer_nft.json new file mode 100644 index 0000000..e54c346 --- /dev/null +++ b/abi/summer_nft.json @@ -0,0 +1,54 @@ +[ + { + "constant": false, + "inputs": [ + { + "name": "recipient", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "name": "currency", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "components": [ + { + "name": "proof", + "type": "bytes32[]" + }, + { + "name": "maxAmount", + "type": "uint256" + }, + { + "name": "minValue", + "type": "uint256" + }, + { + "name": "currency", + "type": "address" + } + ], + "name": "details", + "type": "tuple" + }, + { + "name": "extraData", + "type": "bytes" + } + ], + "name": "claim", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abi/vip3_nft.json b/abi/vip3_nft.json new file mode 100644 index 0000000..8c199c1 --- /dev/null +++ b/abi/vip3_nft.json @@ -0,0 +1,862 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "level", + "type": "uint256" + } + ], + "name": "Attest", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Revoke", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "BURN_METHOD", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GoldLevel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINT_METHOD", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PROVENANCE", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_ADMIN", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_SUPER_ADMIN", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "_tokenIdCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "_tokenLevelMap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_level", + "type": "uint256" + } + ], + "name": "attest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseTokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "addrs", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "level", + "type": "uint256" + } + ], + "name": "batchAttest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "addrs", + "type": "address[]" + } + ], + "name": "batchRevoke", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getLevel", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + }, + { + "internalType": "string", + "name": "_baseTokenURI", + "type": "string" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "level", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "revoke", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "uri", + "type": "string" + } + ], + "name": "setBaseTokenURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_symbol", + "type": "string" + } + ], + "name": "setSymbol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + } + ], + "name": "tokenIdOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "level", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "updateLevel", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "level", + "type": "uint256" + } + ], + "name": "updateLevelByAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py deleted file mode 100644 index 8476494..0000000 --- a/config/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .load_config import load_config diff --git a/config/accounts.txt b/config/accounts.txt index bf871f9..4fd849a 100644 --- a/config/accounts.txt +++ b/config/accounts.txt @@ -1,4 +1 @@ -auth_token|mnemonic/pk|proxy -auth_token|mnemonic/pk|proxy -auth_token|mnemonic/pk|proxy -auth_token|mnemonic/pk|proxy \ No newline at end of file +auth_token|pk_or_mnemonic|ip:port:user:pass \ No newline at end of file diff --git a/config/settings.yaml b/config/settings.yaml index 450fd18..d1d2d18 100644 --- a/config/settings.yaml +++ b/config/settings.yaml @@ -1,15 +1,32 @@ +# MAIN SETTINGS # referral_code: C4ACD869 # Referral code (If you don't have one, pls, use mine) - -eth_rpc_url: https://eth.llamarpc.com # RPC URL (Ethereum) -sepolia_rpc_url: https://ethereum-sepolia-rpc.publicnode.com # RPC URL (Sepolia) - threads: 3 -min_delay_before_start: 30 # seconds -max_delay_before_start: 60 # seconds +mint_rpc_url: https://rpc.mintchain.io +arb_rpc_url: https://arbitrum.llamarpc.com -min_amount_to_bridge: 0.00001 # ETH -max_amount_to_bridge: 0.0003 # ETH +min_delay_before_start: 60 # seconds +max_delay_before_start: 120 # seconds -spin_turntable_by_percentage_of_energy: 60 # 0-100 +spin_turntable_by_percentage_of_energy: 0 # 0-100 shuffle_accounts: True # True/False +# MAIN SETTINGS # +# +# +# +## MINT NFTs ## +# available: mint_comm_nft, mint_make_nft_great_again, mint_flag, mint_shop, mint_air3, mint_supermint, mint_owlto_summer_nft, mint_omnihub_summer_nft, mint_summer_nft, mint_vip3_nft, mint_green_id +mint_random_all_nfts: ["mint_make_nft_great_again", "mint_flag", "mint_shop", "mint_air3", "mint_supermint", "mint_owlto_summer_nft", "mint_omnihub_summer_nft", "mint_summer_nft", "mint_vip3_nft", "mint_green_id", "mint_comm_nft"] +delay_between_mint_min: 10 # seconds +delay_between_mint_max: 20 # seconds +## MINT NFTs ## +# +# +# +# +## COMET BRIDGE ## +comet_bridge_wallet: "" # pk or mnemonic from which script will send ETH to bridge to your multiwallet +comet_bridge_amount_min: 0.00115 # ETH +comet_bridge_amount_max: 0.0012 # ETH +## COMET BRIDGE ## + diff --git a/console/images/console.png b/console/images/console.png index f391945073713a233494567df853f741ae1345b0..9edc6db9f3421e28dfc55e0d063f3952f8048f60 100644 GIT binary patch literal 39484 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 TTf9L>#W8rg`njxgN@xNA%X>eT literal 124212 zcma&N1yo#1ur7=S5AF=^4#71*aCaTt0t9ymL4&&!+}#}#2<{r(b#QlmL(aMKuXo>F z|5}@YJ-c>ScU52?RxIx@8@EoWun|49de(Q<_7I`e%Qd_-&aR*o#6|OG|FG$v+ty*Lm0V>7E~o z{zK1PCo+X~O0n-pQc}Z@DDPkpvbD*mp`=NXOj}yUoo+5)zkXAnDN@Kf5nB2BP(FU@ zWGA>%)LPmKzS@2Pf4KGh`U*`2?@#*g<Vh=);=2Lgk7M`~ReuG@c-ml${il>k_!Ly;wnY$#4!^3kPYiGYc?qQ=E+}Y5PR?}t9iV#N4R@F;I_^ws zjJVofbNDqVrraB(Bs7?bI6_F#|D7Q!^sz?qLy2^t&5 zFGS^^bz)SBbyCZZAe=2@*R7fOpmgc^tA;|1bB&GvP^j3$MwnzzPmTAM0WXX1(3MWl zE{Oi+C9qB@9m6-Xzf<`DR-` z+D~1*O6N|?^rIV14eja_!)JYU6^d|66FL%b6X|yx3XdjEpdH73Xo;uOfhq5GiU0A2 zIix0PxEfdOoe}p{f0*o=oDR63eg|%FRud$au`;>NBO7v^ypJ|Vrz7r=>S;BU zk@p#}daB3z~U zWW|BBLSX%S8}CZWn=&DC$I!>2J9U|oiSWmmB_xUzZ7wX%dd!*{!EdISzrS&?#D|>DB{Q;(t?mWqTt!v)K`r<$} zNwEg-v*etvki=`%(*)c`nsWcN;~Y-zjL<5>3A}M zpPh)*v(Ea21z2CpFHb)i41xHBiBN#xfUw6$dh)S{ym-d?iNCJely;5Q{i{J9gHMp{dLsed6#A1)q#ikQ zUX(yBYD{v*)g{F%Y2}yYtxnE7xeZU64`oCtf|@{=#D(ELUQl}XQu|A$+0$2>u@{XX z^_E{Q<8a{BXXKIELV6r`R?Z=H;!h($i&kpIeqh{}zNK}uQXEk)79~jhn{?09l4eKOOYKDSn8TiLKB z@5c){VMSkKX8{0QU+aoR>)sa9e0zWngf{m8O^?^da|x|(+ttB4%Bb>>VPPuFj|tLSn(~GzrT{Ch=Y%E-;j!W|Q(vQ3uwhDjPvU@G??B~b3|Cn0~?OPjl zx@n>>AG)sa5_*gN!$?Rfs~cI$n_rl^^E_FkV;2`lnR6=EEJqW8gsB~3Q71;xYC z^-NR`5m-pdFkl(-Y$(O36r}sq;c?{B2L=-Bc)}Iu6EZNoXe~!ba^kRb-tn~l2_b1j z70(G3qm}xS9)b5qAD$wg^ayRfv-y(z1Wi%q+fpt!Rz43Fg~VqX8=&(v6Ejh~g3-^c zK)n>|S%yF&SgW=$e^ze_<}FydpyS36hk0iS9D+V(SfOBf>s$6*P@i3hqAGEHfxNCW zfRm>CT6159Z{d=#Cv++YPy8klk>-dDhgU&4g%3f6za};k8m0}W;Ne#fPJdgh`z}Q| zsk}qCf5OEt`OkLcaZ(rs_r0_!lFW?UQhpwZ|7_^4#Fmb=>Sub}=K&hZDd-}`jX0Tq zLL#tp(1_LYwx9Jr(T~(zgL-5d_6ZP;yG7FPK+f5R)|80LixP8$B<(_{?zegvHpLlB z&RsZAj0f+#aE!a2{$!gad~#KbfU-sTvk`%Y`yL2y5FY7%)de9w)tG(387K2+EWUEi z*O|R7f>*iH*pD?g`S)jehjsgrhJ(Pspm#vmg)lqxqm(Z`698N&cw_jA$mSPHHbi^s zuLa?d9zhOyjV`}r{eAe@bf9kj;A6m^*RMoR)*|@<0}OOH#pEhXPXr_fBS%ex;Aru^tFh6xwBY; zF)8v%R36u(zNwf`03F*&K)eOjZcWfVJab%FaR0} zNE`db{V^Uu2yQgKxlz=yfUy`T`_*!RD4^o%oG~IyWdu)8EyCYJ@6pkC*hntwpOb6? z6w*{dnN9ZU!rTf0Xc%UL+bNEI8I!&T4b`0c0~*2Jn3QG&rvb_LOYWAc#+7)Vfn$f3(JkRHIB_ z=)(V`?<|L@D4cc~t5yH!IcEMPL{`#)&6+MQ3j&E4t4r2i_X`O=#}jY z{m|OzyVJo?NcjT?>Jr9@MGHPL6^Y_^4sSSV4w~Y@hpup$MdcM$vK%Es2ri%M@&g)o zq4Y96_HwGW=eU5NrU=ga?)RQHE+ba((oqM+GrOe-3^Rec1pe!oSj|YQ zvOwGBkD=CpX2vGt4`Cv`;zAFC&R9CqwrxIt$8{cKC_*Hiy?uBSwxE&YCk2zrmKa9A zS!};11!_I-JB;KHh6Hev$39o{^ztoVC_@>J zCQpo%_95lrTiS}x`E)!fj@khXkd6$ikGkN?RL&9GG%+2-g{bLuZO!Gqve*n9VhIcm z(2uW${>s^v9qkai-63YA;%FrjZ%Eq!rzH_MCg=-q{LNg?>=Q&bl&uACLp8~Kop2x~ z#ryp@=lLjp#tdV?lmnn~EB-m<(0-ZfJG4o+h|uHH` zz&P0(a&m3Nw?_t#3n7W1;IqG9wS>_3&ym_D=+v_{5lQH1`emF7X>4~Q@6LCtCyq5p z=m=r>J%w6?>!b4d~b(B0=H>Xdp#ao_xXMi>trmp`FMW@#h%!Sp5Q3ZjM=II_VNB6wojb_}V@pWP&Av@_+iuM5F-_oO3bq~Sw?LKR+u?CH z=r~&&k+Oee)rOXObr%5!7omXCL=R!dclP=dMQ8>>{m3Pcwlu2|%14IH=i9ZaD8ZZg z)JF1Lm#aZVANDM3;)=W^4`9|?*d2-Q0=jPPp_=o@@054X(UBdiU^@J_^fiMjWyI2U zOFSsCC#he3n{t$em4Daa>swmGs)*>e5P3OGa%DkH z@QApDVzC6pFN|NkIX~i-7;oK_)RTC(fcBq>_PP27$;$?byVu1a&7~_&y5kUC`}0+i z@aU2Ud-NgZ+dlJ#XOcWRUCX$iu^1;4)erF;$_cPjH3% zGJ{_)A(t%A&#`EG$3JH2z<-#e96sPH*9=RoW0Lq^$)rQ#-Az-+=n~2EB+1T~)O6Ry z&+WYtP2t8#b2M+;!(|~jEB)K{q~Rt>we=kHYSJLOB$3e#PCw(nev>bWC#VpbVopS8 z>YS&nbB*&>#gtbO(V}b=sKi)%K5_H#T6X9^cg0zDnFE^Jy^L)A(_4LT20M8`@&7~w zIDY*2(Y~^{xLNUcC!JP)$d@RK1P|grtUglJBN?xz#lg=_v#jflT?5n|?m?W)i{cAw z3OB|1cL3(p;NCI+50FB?dY7wN15npOByW<{>(YYw-xoxwIWfQ^o<(e99A;`-?pk8M ze*y`QFNrJIat#0LJ&HFd<9~ugwo2`{Vf z-ArSgh+WL7i3t$MU)z+JZgBZ#|-{&;iV ze|k5Ya>bRlF^T6Es?iM%{Q&Ca)x&E9TFn#NcSmxc-6X^-hVtqNai?8o$bocmgdQfN zQ-0bAJf?%rZt^s-FEtYuGOCt|UiZ{VIKk&^P`bsBpT>IXXWrt^*k-HeBd4cCbxrQJ zt^3rgna)$v?M(4ym90ZZW?S0SKp~9@1MTeVvpkaf5G$@92cIp{jchE2i>iL5(cpRB zSG|9Gqb$K!;AIf5B_Q)7D?D0JiA|iG0yXt z_4fTN?#LZ^^cL4nbk@FgAA5Wl>goH6}L4EDol;Yq`VWZ*~+2WXdlaD__)3J{M<(?;4ZH#1tjo7NiC* zK*kU3-~T(5I3@ZIob{c~V>3!gat@c#lUZ;^mZDj8_>864U#XAo%8vu#^; za9kG5lJRwo)MK9BA`tkZj+KoW)4keJ9%3IpdGK?&6}pIxE{=>uxnAmcUUJeMg2Wny z8|)wpAL($B99nR!raujKVPSC;2J0^8N^Lz7J^K9H;&#{MD3g`sZ9DO)>Ha9kmZ3(+&fG;{e%# zdX;Bl4fby2cEFoS%Hc}CoKfF@Y_3VD)RiR%jaiX za69gL+p%#1{_QANDmcu{64plY~aId7c>L8VrW(p(CZu8QQ5Lfu$nyE(o1&#HhB0l`7qK%g*JZ35rxmX zl2y%{h$am1qByH}ecazGoV2*4wUFab-}bZWRw<IE2h$zAOJv&th1q1yBXE^26DezUy3X7R4Jhv`KE9l7n< zrR_5Ec7wDer@r@BHnCtkk_m@@b4BR#);g)x*NS=gRaM%MhG=={wc30xs9vG506!f1 z1<+8Zh6vVQKW5ZIS^Ys5wC7<^G5L;#Ka1m8B!hfm?T$isa0;Lanr>4~pBr z+9p2{Xq{Ac#N1yei)KE4k$l~y_&pn=I@7j^-^#0v*IcI}i2(WC6$GN%PX!t8^G#iQ z9`G{go>ea2p@A25IC7M>%;;v9;YYl*C%J01nm8?kd^Kc1i5lOBt$u&6)E&Iq2@3Q2 zqqS~cv!3UqG|*X9oe9>7ccR{OR(n|eX`ERkg^odK7M#;-%zesYW{mRr_A$49wEvt` z6j$H*RGy=OUa%Zzxi`53D`w)o{KocRh^^2quKfxq-RZsEoB0aihN%{2c^UKSG0)Gg zvB%|Zsc>IX#=~s^7m%m33Poj$$FnIl^S7tDooxgpY2hhB9iX7pSPfSrdz(o!?kF>N z*9v4ci=Fy3-nf%G!_?siIazESDKeS1pTxp}6F<2pCuFTAbFt)SMS3~AnN>ne?%f$B zzNWhs@@`!6o*6+HRtSwqyg6TqxY6bPFzs=OhDvlM4eKWo17O9z+rrq_tqo4xJ4^K+ z&q#JNmyje5=gj33{IsF@sJ}0BCGR7&Bg66TuxI1zYmxYgbe$K>lCBmw0azW2?+~H{ zT=t-RCVtj&9iC+PT@d^jDjSaJC`pynuHq_|lgrZNOZreaeNt>*muNL{jhijo4+h-! zI=FC9p&G8>rhLc!x+Z_@d`Ls2Kr+Dm)~2xrUOD2ci2kgCmKkm5eS{wP1=qGz*Y4yn z9V#E*VNxhj!D3^ZEGH9nHdRXiOHkM&Jr~9&l{ExO?kH@VhPRHLv468K2NN3go?8v& zvFpe%*`!s=L`do^1OcL$8jqN7>#VJ`ythLsT1F4y64j$&j^yHVtKO+;yi)+^4Ga?v z^Cgqj3BzIyd4IJ2oQBYm#w6kv1&K7p{DaJsw0eamNHR5ety+?yh**^$$_#@<>~<~i zFt-eJ7)=%Mjd~Oy$kSB?*gh%pIVfs&!7?a4E>G|$P3V4Hd-YkZbYY+qygh55Tu105 z44wp~`n01QpN%@q0*o~HNk$%7cVbE8w>-8c+j-vR%%yF3#3Zb!k4AO*@De#ix;*905z_$BkO5o0WVYFh z1!?N+<&Ob1{Yjg(pQJR3!DK1G&Ti+>ULk_HfQeR)zvn_#MBU zc-yvPF53*R(3ZK`$eaOImKLldS>~ste&jndZhX(|SuPj1S{b0*#%85))X#89sN%`> zrbTScr-Ihwo7ai^Gue*vcD@!6vmvLR?>Sm9rmNxD!aV6W7sR=9(A-r^u07Gfb3!7g zf{B6vcMwg%%~NNvBEPbHn)GuFANqy$<=5_QG0wuWY~dsN!j&Fp00lR3EA`?N<8~h% zA+tgYp+i73O=6o&z>%}|%{uW*cQ=;OOv?!$K(@^&uIqxnUU5#@;dx4^a?-U*Hd2xe zW0-k?=>fXeI};eo-nkPxm3nCA%*cqVAk=J1R|au%Fbf!x677o&!gWH8{D_;P^WrIU zP3It5vt?X4{s7?5is$+~t~m!}#P6;d|IBTvH09)(n!4t-=r>Gx;g5Vl&g`3Tk#XzH zkE3fN`2nZ#>q^>UuO`J8*&!u2LE?!TTg~euw%RJwkqzb7m5EoWrj3M4Ew??I)ei=# z3SLu~Gy6puNo6ir!#CN+u`p4CQ$qf%+ZV$upbDVI=i~LThD*x&wkslk1g~G&oPj=Y zrDfk;FphbCD3D3o<0%#R9{>VugOM}M`h*DJmliA%iv=1FSi{^)kVn z?ZIzmct^$2N0rkRdam$g2ri$FzD*sp7^{fp^&N5wd8ZfPWxnEm<#R-uj%>RJ9I_?o zuI5$19|FAYvdi9nRB1WPVWjx7m)0ENa#WGi3-O6^h`-Vcq9&v{bxS#}i$JM0CmxrY zt;PpB3~o)f@K#et05Jv}`GQT5nA+0a#$Xp$RI-5Eok@^cUAY{9t9|Z^MEZT3;Ccy< z?}VDpX7s^z5}72zYXFI!-%`9v*~4D^HSppX%mW(O zL@A+jc%2)X>&aaOoM$x`b5yn-8e3=~?A9jKyP>kcRovt8l2caHc(1UOXnM@7BAV$M#6T!Hz?Wrnka z(!D=D8XjW@KGzC?k#(V11&b9w-zU}Y(*Xtj-1=P}#Y!;FmmXKy2N&TIOX_=M=ap{l z8MqPsj0vtp`;=!;zdxx0!epJy{z16AThbL10Y z>!c5|_Od+#qIv?~(p6@E8|q1IBL8@v8^=tf=;jch&z7rP*u-L3sY)7~>+lsVIK$dqMaLokc|H&; z=*pj8UJXahlUf+_LIT2Jad z*@hOy2E2$=*JN+mA3AT%kA(VW9N4(uiNx(U@?sfVQbPJ$}2;%Th@3pj&R%oh0g zkc|;0kbO&HzLp(!9XWSc%kA@3jC-(xOxji{CpRpq)~tBj5tjkx3suyn9}d0TL7aGV zL8y!_v%{BPsyb?7UxDy?54;$2)Uh#a{c z#|ge+W>q14#*5=5@*n>8@g%~Xd4u;S;z&_Bp_0)3*2nDy_6ssw$2Y%?LJ^xrN&v6V zT$se;#>HbCof;e4Ue?lCs>ta4ImDklAcIJIeFaVVDGM*uzKYVCn3| z=~5^qG&6Fsn`;?-{L%VFhf^i7(+uZ#kPLv_Ru1E4{bNUqAh)t(6yaQ&I5n_a#wn4{ zIFmL{5ChOv=tV`KUEDb5Z3&RFE;in+Zh7OC=;1(~cD@VeEZ+k3cck76RXm0gBEps3CGe@7i zQQ%^9n`Yb&Kv!X`O}8As%?QoN;tF%_ia=wAj`v&z680NrS`*k!A@k%^!`jkNgiRZ0 z4?!#v0|(9Q=w=hDf*2($Y}1n!WuHL^@*W@EMSVp>D%-%&KN+kVdzX&ORQE^?2q;ih z4-jg#@El}HNCDC4w#*&s!~B#{_9IM96=Z>yA-t0e`pUzGCIr)E_zhkiYyOMM!#EDS zjKFJnikvZ)$!{x{MU`IkmCi;~C0L9KHrtjip`Wi)`Yx#}F=ag5)x%d^%;+2z=bZ65 zZbE%$L0b=D>#kqmw@Ts`fwR3rG*HOAyVD&GYGCU}iV_+Ns$#5$-w`>3PknG_ON(X*$uFIgU# zYwiQRxjjsDbvOI`Cm$7zFtGD+`+{*?0+^$<5?F{7H62^RzwGBaWpB!z9vJb;gk^~t zm*H;)IOqZ|!rAg^%bW#mxraY>OxLxWB~N*n|D9GPK^$yZc{?#fk^vXa<#Kru%ARc| zQt0~5BdkZ0I`4bgaOMPdqBnz>e=ozv`ov`8N?K*u`1OvUO5}u2_dLbw1yjZI3oF1Pyf~(_88+)ZkCZ<<%N1T_AAaprE(W2JxWixW?Wp1{;RjH zg_;jyuY-Ufk_TvMpE&}V@gRY=sqA=D`r!Irq2+U%nnl34Js*Z7Ch^IS2>|wN8k2!7 zS+yzMH=Hoc%&(1!9Mgi<0}tLPIjq~fK(y9*j|7*@$kYoOMs8ZSgxwtd6un=uE}{!c z8>gMO9bLfJG{9J|QrgLDh+h z1M(1Sb8ooz%v`kHNHtAQKq$QL?}0D2H=zs4uEJ=`mIqT5rg{%pKEm16$_TkK?|*Wu zw@JHBBIT^@cCSb6y)*B8go&S%W4G5tuHRD@e`gYNxKr?0-v zo&KF<Cu;#uE!CN@4n!=&{37Z45vnhicuCyY1P9ly-amKPHtVG9{V$T9k1yywJ#lfp zKpj0Wz!c{95CbsY7Ro!Go7LzS@*4V1;_2DMYK!NL?w<1<*n8FA`3H0`Pf4&-OVM`p zU1@b4n%-(Le)Wrcw0ujqz}nA5Fz?#?q@JqyVaKp|>`EAnTA&VO7#}eBmNZzxxCi zRoAH@`Mq}zczxZUn1u2ALkufBUe>UZTo&dn<;+MW&@+iiQb$I)S9;9#4P8a~#f;b_ zFug;np0abj1)T1?J>byJ)ya!)f+C8cnSRRI+3C4 zUiDJO%p14oWQ&Ix;J9AtTKM3(ToD)&PH+>F@zQ(jaU%s}(dsxg|K>`R?!WYPlIZ?$ zE>-@A>Oagv+;qguU?$Wy1nm)912#)C@3#uCzznDzCVqTr>Wd%ynXtTD2~s*dSiC1= zvgSpNQ3SiHvvefXi9aSrEiu_PyAeXLURKir-rKz{qvv zajgi;>0;qRQIWgzh)QTSoC^NW7%;O`F0;UNxX^H!-TsAB8JpW3s^g`}n@P zQR=X#kqBb-RdB(Kjl3CaT_xe_;)gv3eVuo}%;|?w7BXCYcQU)G)&qJljzgL-_+L!m zWk65$XK)S9 z{!94Pw!XeRzJY0hslFO20w!;4Aa7IL)FoaWtHP6%)ji3Uv zUk_`J!)TK3iUZ1UBet_^2A_h!9UAfPczx^u9n0!pV7%Rf0n$t`9$RR!LlrYtaN z>cIde0g=F*-iu!oO_n#i-QP+K>N_$z^tZz~fMlTaG& zH!2CnNCyM9eH{EG=-yWCR!Svg+^1r%$@*88AlZn8U zv44p+a0BM3)Lc+)uE+l}VTb)a%x$ee$5=-+HZ-sWXCl==l-|TT1Hzt!&&R7)};CVRa>vk@n!!K zVK@LgR}$cY^)|{~zvI@w!d(xs+A)a!1(TL3f?$w*!Km$Ev9j%NE9E5QQb8l$FPB-M z3OQR!_%4C!VYwB@VW#%K^n}qfnh3pV-y0PM`Iia<*ZQWwN)mgR(n<+GZJ=Ii{Ds}G z#bCfDC$wviZL-L4!Ff||{`)fXjSJH_{mX^@V{~D#(Odo-s|D7P0&9`!L2V~|U7~pY zZ)2a{8k_5Qxfa;k8KlW#TycdJNkQ1F47V7$x#1FadUK12mcgt{%=_d*x^b<|sL?L) zM*+U!CItSmJ;B;4lg0Pcr|Q{!9t}gA2)NQXF^E zpa6`~r_FDf5rSWO6W~JnE5+#0Z*Bzs=m|lmkcdbrK98DjLS5=w_MVn+7d!`-k?~u{ zje%loa4%}gD#4{??z6n0l%EbsI}g-sr|v!v2W1{zvB|FoRi%fOZDaFM-rC4U>eG7^$O5Oagw&VmXi z%UXsrB>?lQ%9~x;^JnG(>MVIGtcaFT(grWx-`TlN&XiAue&<&@a|kBA*i>n})^wFr zRF9`G8HiBTBtVgpb11Pw=gT1$8cowmHy-Bbr@Y$>dQ+ZJ?pJm|V)H|1rhg`Re2k}} zv>LtN!L4WM%6>$uPlsY|eIu!|Ojkf)BuPvsowuh!xOLVxQfPrdb#2@RgmgwhkvCqafg?UG3AMrhs6mGl+WzTDkjYM`SkS~>k1M5y>zv#GY81^@x3jid_ zztwIzDc{M84K+mMO_x4-|5LD=f?8MccuFh4DYcihwC}2&z(wHF&{D%@&P9NtUZz18 zs6AnkvwZ5JUS(H);g853nzzYhm1;H1py;8gEt+X&{O+DzUOQ*cewZm#>8|&gV=q3( zfcTSU3?n!!f3jYd9%KgEb@?p-p;W4<-l5u#{`$MRyGm=eVCZyo4jKqFH{(89%Pc_E z$mZ6s*v9Wi;1h|-Dix@(RZ`fCXV7mK#@Xq5^IXco0kVZ)CatsiR50*wr$_=!~o@w+&@Gv z(Mqv|8D&oQDoc3olHGXYSAxl1tj$z6A*o;e6#L{8n5ydg&zPFwU0x*;r&0NH!KCov z7R!KrGiNs6eGX)i?S+h!Li=u`FgI5;s)>= z`^((bgQ=$=5I;JI{$THyv_EI@AN2wqWuJmlD2&-e-$0Zl^}(np&B031E^;g+u?{04 z?-Lyw0Y^n2kE~~jKBfyT2}~Avqh^K);R&SlOQmHGVOdsZK=Z7O{Nv7{gg_bF7DNB? zfpw|I$`zh*h1R;$GU39t2h&jS38 z*=>%1cTYKl>>=U2eV@&b<+QMAn94KEIG*ULf`0Oy2O~^>2Kj<2vFX84H@^luPOI>? zzIt(ep-JR49Hlh8N05KGQ06p4-jii+-`n9D5jnYZ@-bs&;ymMqQO`}FW5}uDPl*D1 z^ab_E_m=IIECbinx9xX^ESM-)|G88Xf2kMZd~V;lxQmNfrUFmg49!GXQm43Zz2hnQ zwQGq5=$6rXImYiHoS8zi4_~~j(%Zg71{wY<{pv@c|G4X0jXCX@seBie0$h9VU~o9g z1F%-a9U72%jikKcDbX}pxzEkFH!0Loz%c@s5!^*&uLYQ16lbCDS!tH)>b{c;A%e$< zvPcd@0A#ysob5W)S}s@oq@M3DswgV1Rp>(4XC)P0A@;COH%E=`Nw`chaYHk!Y=cC= z)BC_oz~rlt65_>m%#~)5L{5UhQ;vMUSy)EJg<8nSfJG=1?!Y#*&XHg)7^1F6KNUeQ zxLg{(X2YHRHIQ9UN)hKb);HUVPu5kug*ndT{Ep)1qEK5Au{?ND58sMhbpraZo z5fB{qg@bgnc+4V-1^TJIWJdn!`*}Ha61V3|4x8T);kWuV?W&drV_U^phN||srtz;Q zP9AMio1gZ}duV@+2;W~#d%S*oihljQw24RNkH`x1N$Zw|48m`^UrW8`2k{sJul3~y z@3;?_9a$HTz(tuj^ZhOX$LVH7Ywf7;o}Ys49hS$wHDnN?P+%C2kD9h(8KqdK2s{~1 z0u5g#r3}xvfko`q8r!yI0D;F~;-h6d8S+zpT2r=fmd| zw|z5|abkhrcP%_+jD_}P*riPy%5n~iCs*p>o7kG{=(k*f?96fVf(F zGiA$V3g4u}-+xBeESoEQNKq%&su1N4JG=aTF_X4^Q8-LRBJpgmu_K?t>(oZ|3a;Bh z?fCVw*#aV1>h*&<3Y*dnQMR9CFc{RWkJ6U}5BtxVKWm$!2YhA@VP7tvR1%jvY$QrP@ChuTvbjTBq18Wn>cgWJY}CwvJvV zv2^+4$Ati60_4Km^mE9ey=B5k!yFeo_a9g&WDiye&XFJf!_K3+=YM{l5nnc^F={T$ zxnyd&w9$&X*eH}acv`f6cN=z*`=29dwzHT z7iFb1h4*0>)S>0w$-IJ#12b-m>;O#B3{_9?c3VQoBd;Ex5=Rr_9mP3LctDu+OAjL_5&JM(w24TZ09e^}oODpXRLZ}2#bY5+WhY>8vHjb5kMzq99~2;vHI;)m+9y^S z-j;Ost4(L$7fQJkf~d-~pTvM%r>S9`jtM_kfpy!HEeW^Z3IP{ufp+*P2AEz<@!4`n z{f#QiF6p@yOP3H8^_Jo!X&aZ=bJ5lxgdu@Nwh42eO`ZdRHMFO(SlS;C_t=HpiY!tf z1d={G~qXlzX+}w7RjOApdHR*bE*W;a+u~JvJpV42@m@)E zF`J?9Ogj*V2zWISM2)T%cvZw-N@~e~rm@-*S9XlC;uub8UK3g3o6{xR71O;Q>NOyK zrE(d_jeZzFzqEG|4y+6Tp_VXG@5*KhKu`!f`sVk4l>!+VVZ@{I*~ZiNpYiB;m>LCJ zho1*m4la6L1PD$u$Z zL_idaM$Kja&UEC=iWF3px*64c?oMhV-18~sa;UeZF=Bppa+swfy~!F9`vDO_8tFta zVIQK-^oQez8^!`>B&m>DD8aEG&!lYk`lTg&pFYbiww~@geLD}rlA~OP^|Kgz7s|qT z{M)3C?&Qu;HRmuKc1{ty9|gm%Tzes!*5wPzeeLbZTN@+M| zkG4!te_-#c(?{CK?F$Zy)w8$4wfvN%Z$OWkUff`V{VMQ3+Hy1wcayxHXYCHad1jwY z|IpXJ>U2)78bJQ(y}+_EFmLt6FStgC2uYBZ?46~ght@$LJ*?+4RUhZ?wiBzap5}^b zU7=VU?3&5%v@>~%tR`&ZYac{c&s3^6NAqFz+Y_zii+XcY5?ErL8+vvjKZtARTy5YY zxdjEW0v>wpQ2UR)f*U;>bwY^X+Mj4mx{~w_rvNOUesYAr1Q$SPZ5w;r}z=zQ4_ZJRbBp*Y76(2J{W+R&BgYKj0;BryH`x!q)LyWGorI6y)ccYOI_ zkARf?aAm_vzsvq0pSmEg$U#F_t;gl(4oy8H8%+{5FNAF zES|6l67c(aD$jf%kmKX+zLq2d;Kja`4pUQ>`QdPdvOq#mi2P5s^? z(8X2=p#QL|M1FF8mZIt;=&XoLe!9iO>Ong`>dW5$xa+G#;8ca!W?*n!|8xkc58qAx zoDLEQZqnA3Y*@QbJmWuYm3^KTe#Sgi9D`$8yn7BJOt#barV6l^VztNAe{X9jz&h}r z$+20(8$Ta+E}uc7NlFd5*;oPFXG!Ke9HQc= zT}JwGg-Pv}Z8khkZfs>@zeaBD*iFKF3O|T?c#2U{_6mNdkO>2eeHfxl*Z#3}=4;Q` zT_W@wTnvP)oHL0kSQ5Aa^q)sedOkMR{3cz&THGFEX{O3{ihLD5`j$$c8z1wO+IhdZ z{JQLiV%J$;GKsIL>#o#1EVkv`0^lZaWBh2HS0Xr{{Rq4Guzy`#TlOUt@~L`j(yRZ{ zs#TUH^>p=F(4}Hu{BjBR#n!T_?P;oxT-H1RArabJF4w;ka~}CrQ_!o_j6)x~a*xE< zFubRUFsVW6bG}nGTK1)3Ev^@F>!s94x1{u$2O2@B*~nAr7yOZlsI|Ldj;S;bG88oa zl{m9<$!x@SC*lO0^yIPJ>CW>2P}R~D!WkrUy@?oEPbLiPKxAclZLN>nq=NGE!}oeUEHfCb!xB2{gg$^Kxr11eXFnrk#>NObxzpE- z=5_{NEmU%{k8+yR1OV@%qAsnESo6s7cA-~_@#aE;<+shns0x>I?9sWtMNawCFYM*c zfbQzmn=GEEg|fOe@{tkd22ia%4I}P?2;0%}Py_LrR-#@m@LD6~Q7?jVp}P8-RZLJ2 zgZfkI17n;vc`@|izIRa=C3BAVjpyO-Y7gE+#RyTa;zUX65u=%yQT54_do!!K?|EZ! zBV`RAeXy4_Zsng9MS2av75qXDI-cg7XG`3`u^n~5{klxBHHI?Y7e6)Jy46g|+Dy?c zOIOoN>&~kyoK$m$GHGqp;jDnfa%_CV)O~jCa!~L&M#F@cANP)cC&K|CVV`EgoUXF{ zz8anzx8hmhT}7VziE+}rixq&1*NR@{^L?-YSDP=a;1}gxpx0vm1u9qjBwERKHo4_U z)=N!h$Z7qwqH2-QerUHzF;UtDkK$c|gW2x{d{!JLzQ9(vKE2#STg&Dm*@(w8iEbc= z445)UNU*bU$gKJJXw^2Of!ef}8To1A;s5Y-l~HkQ&6eQq?(Q1g-JRer!6iU&cXuba z!{EUO3+^7AL4yT%CvS4^ci&pGX3eke>2vB-RafoWJ6aJcp&o@-VbXi!1mSJgci8dR zAxCOk&m%sMhoR}ziiQTo;bDIfYLAu(fNRZ&B~YzlzP)#m#a^ra=8O@&vw>t$QPJK= z!jCYKpqOxd(CT&_*DKY#jBwU0pKZy@#8Zde*w`-35*E(RDL!sN*l^_3UEWZN*!qa# z+ni!95YWI{TUsgBW$eZ_Ji^3*;fFtAYuSZ)~Cz4Y^>tf4li=G@in zRhjx+f8#_&0gAF~8k?6BZkYg)pc(Qc*2O@X8y)ajHl5sK#);yxN(0t~`yhaLUBJ#d z+K8w>#y6Lo`#8k4zn5j0)E-ScZ%KKrKF656@)da3&{v4XdNN<`IZxsVD(i8e4F_(w z{kgf+U7O-&%AHhYnxTrM|As%ZRGA_tc2aDg5@tZy)P5>thASV6z)Vgz5*<6JAyosf zSy$<|vCk5E*SoZ7UgRRet5mmq^+`n;9_}CGFD6 z#?o)*;j_o#eE@En#cnl>xcGUu)PD8AxN-l{nou{c0PVB7}-7>VW`_&JZ%lS$oTt7pD74Gb)Fm;`x5mra}H?b&7BqVGF#B)S2Z^R5Un zeF+sww=~gzAFSQ?Z@cwAtNc%+EE!2e@TCq$O>K;xz0Mg^ zwP##1{oL6pu3@U&pL$H?_HVP}n0Y>X}MPrYu=(-&+1F8wLmBbmL z(2PFhmTOF`Yj+?pe|zlVwCB_FMkMp+=o>$B*riGmWN~9iv3BAC+yHTr&y%Bf!LMTfSlS9expYPk65C@Jnq*uXP>PqG(6!2ZZ($+_R zPrtDYGf&@nw=n>hYi1ptIg!VLV*FAK{xk`q4mhLRjg6QB#krAK%Kx_X0GUtVU!2!{3Az5ZIYzR~fi0a;mN7}Ge2Fk3J& zWFSZYAvI&b4L_&ZaLwNOvoKPh*iAMssKR~6U;Zk+EtMe<(i4Dt3(y|l@)lLi^H*LZ zp*mba(gVxcpYod8RWhX~3njaxrXiHLoLN7kHkLvKUgod}lSP?fuu}606~5*jnYDBJ zimLUP3o9{Kw}_`pdD3n~7|55}56#p1Goly@?o!3P6TC-kdwuIzYU`d4b4&h8t?$1IDiGZ`olPvxKFKeYBp>2NX zE2MB4y#`$w;DO9_r&5wJiEwBS-VuV#NsGW2Zx&-lK_ljc-TZ>_9Aow1`5FMoPL!MF zXv&ox>B9^+BbzQ~X1tyW_I|C5C4IAJulDR+nC*7zA7BS9W3^B1otqF)^+}Q>jQJu5 zO2QT!voz}sNs)MvfH^1lu+{jF6PH(=X_9cl0Lg~Q`{u(nGbFzUbIo^39+AYmhfw_De`^^X43o)o zQk+R6O3g>!xK4PJW1o0{LTKX2z!QHr&*po>2vMdEpeI}Z)fy_7Y@i7*3`F(xu2h%% z;j)760#p)@+q3ZElx{bxJ)#Z6%*7YgwL`EE%s>#~S~(FPtvuaL;aCsAy631+R1CEu z+I3Qwx=}Hi$nr16P4w+towpc=pbjz)ymr|BBwB>c$Ck% z#H4zgJDMbe*8nDDBdEhMk1UlppU=DRWd{vYGy^Q;3p}bxI%^Q~IawIL0QW(UMb{B6 zD+Vkq=eUp~Lw={9EwI?fj7t_ECP21%A1GpIPQUwQ;a{>#G4g%B?cpa+lc5m*273)} zo;n^2SU%|*rkw#Q8oWjb$7-MDHpXVH+rH?a)a*p-!Rx#+y?s1{>KQMg`Bm3>{*ec~ z1%moY8q)k+`ovjlzv(~Yosi-L;Xj;QZ%jezl=>*&C?=0eSSFnU*Qx8|YKBYE1pGXF zSTHqyMJT7zaeWi&t--*O^L($(UVQmrZR-iNi)EbHVQ&A7ItVY8mWbOSEatSTX4yf& zx-FDI)at$^5uO_cF#{yh!77U#(QsIIJS0YEa|xi2v8g3)MW)TsS8|*%R>c?nsJq9h z{6bqM-i6O?|4pJsW&+-5CZi*tTKp%~PH@*seyN+|n$ky(<0uVYxUSXJ3?EEDY3(IALm9j}E?SE!!gWb~?(0ttMvH9EC zU+nRJ`PsR7SLbXVllXT+zUi8ob78mZtvl2BXC;0S%!Ra-&;sB)@)LTb@}B5Rljx_w z05H7ABkkji=Z!M&)%{?q%C4jK_eX;WFI?%mFK-j80Pyd_^ED8fCJ}r{1mHRUw}mZ! zGdv&j$&1;+aSAjI8W43jdWs&TV!-WL2+s(4fZ4~V0lynlqi6URn4C_sH=kgV@W z1FspH#yGrR%*|7Gr?*n&1<g z9gOc8!FmUh#Cp@2IUj~OmfB;PH&$JsswN!A{YN%J9N9wU*=n5WcLU zrK7~MrP+NP6S3Lx51)W63}!H*A=IT~Zma}XIh%~U@fQTpZt7DU7!h=eF9djv+*2~J z@&k}hMmmj<2%g53aF4(gDf#3b=`M`z*@Qolvy^fMSx-=fBJhtxEa-%)c)ns7)oQly zH%p`4yqkI~!n0KZ5VZ`w2rsFJ_%F58su=@kl|AX;`z3VC4@>_+2SB2W3qX;RQ^;|o z>JSuT+l!i6u}>o7!AUXbEAIV}(l}Zo%3I}Q6B)*T5?)l!ra+;)WMJJata#2EQ-iJ| zXU6#-uc0na>BKp*jpS=G`k;~v{tLf7&`3!ZM7u!1$p_mQQ*~^Oy6DkZo^ISUzViR% zebnS3Zm?o;)1T6choG>CiYARo zx;bX$Px5p)BQdO|n*4?D`~w@3k8;hzxWO$;mACmG7A56rI2k&%+cKbddzqzP1H}%h zuMfpd9z4rMzjc>44R*;Vf;NC|Q0EEKyg3}Ae z+e6AH8|uH8@4rSrU2QtVZ0en7w#PwYDZw9z&Y0f~!Q2y_qiM_fCF5z7f5&_Nn(It0 zg~4@J623qe{JW9tJDnMVIAP3E-TTz6hnOg2b7{(K1o<6BKx2kcUpsgTQqcLts)O^6 z^M?=|@xV-s5(T_;AE%eme7nR2Nf*K2MWH}B{w^pefSp~1O6V1BG1F9rj${KZqSUae+ERJw}U$*v$&l538}>b`&8}! zLdD48`7yv2qb(Z;srppP*=UFaTy<4CMgu-3>?C`+DRM}22$Qd#%!CT^maSZr~~Gl(aYv|)os;i`>Lk%WB5O0 z1QyXfzcb$!d+oLdcn_KzXj}0z*$GUZS`gRp=#{J9@yZ#>DyVJ2bUGpt&5l1aRGaal z4Y&$2skfxT_n9nTcgkdtfxBYKAeakC-JRFzX0@8Ht^AQdaRjQo`LA;XuVFWz0j zyM|$q-U`(?Wu6(n-<5qfwFD-5n8*=h^u!oFBgKXyUBUo2RAf=K(%4d2H22(OG!!q3 zUjp1HaYS8cT0U18=ua?z%GaWlvxfNIGO35zNsT`j61{H`ga0UvFOLkXyICeJlSpyq zan$$IKDD8|cCPO=r~e2{MYa$jXmM$&ELcQGg0SkOnBj*3-4{fP?=nnGN;S#jH&SsxH&p zlx@dMQqS%ZJi3Ya+!wYDmBy$VKiDHjNG3t^_G%L`zs!RuOt4<{{%!;UCI)W;;ZUro`OC9Vcf#-1k(m(g`EEv| z)NWG0pni68W{wI+^TuVKBaLT|c|*)7gC(~TvH;L8qn!SPfC6qamn{) zII3gz9~wS3*mCX7{m0rxZY3Xij+!;=?U_Gq)BG8pN3rC67jo*jC=nW9Lv?;V@}aLk zauJo8I~haDRPVBPCd5a~-Llt2l>zrW&Sczw-N(WR!?hi6x|3+|d7pnuVgr>&5hXwA zaa)&$yGaKXp1O60$3?0C^+j0}bNW_>wO^qV8Ami>LIpLStAUu~y;ZIWE&*mnxJlhQ zG#u)5A3|Gt)z!myoaGF8MmPCdkdNMGig9;zwnvDA$S`RdOy^2iDni0lQ0(y*&=QDF z)t;6|_JZi|PmYGTyGZF_dQ`$JH4ij4 z@*>YsM6^t&$@nn>u^V8QgTxF4dHhV_3+QCMV^cUlGP_CKP8pT%Ax+*?ETv$tAsALO zBX3J#M6BspB&46%wyI)BF@;P6*OmkewJ2*Zht`{ii}-xD$SxcDM6&7BQ#ILdVqMA| z$+VWW@IkRvdPLhi&qXugc+)%(n^Y$Y9ERYeVj1feHovq7dTZb?c3An*Mas`41<}nG zkg4vQ2giEnq~zQ)qI$wr~(%`Mtrt%&E1OjjIub z<^)sHW$K#)%i%JS<5c8(BZvW9Do+)$Jq-2=wfe30L?F+rz`w8-5i(r+%BLg~*g&p| zkF?>Ye>OjzT15PLV=fF`>?C*{Cou!CwcpEZi?`{uw z$^na7;&vy7X+!KsB2I7e=?L4Ru))GK@a;kNT^Y2HvZ806V*qAYZ1<_V$&RwyxdKGDt1IFwl7p!v^dDI z@y0_?_p?|#wl4YzQl9sdo{_ukwyy`aKiNnSn(~T9R|MrC#9WZ09?oUg@vwwg;@!>c z*8$^@v$_fM>6P>k;i4HzUa;mw%AsAyGRYSuG)=YfxNE<#Yxjm(Ju-}ZNr}+#a*5WD zo+OWsvgrEP;s?K|>_aREBMrh4_+>R7a&O1%78}ANR*w3QlkQQPosG*y(#U|56!4#C zz1@{s-V7Kt6%y<yJJ zG)xWH$-3JLJ#IRDtSyiW6GcQ|!QD?>XFYpy&8O&uB7?-YPqoQrGle|jtTNX8aEzZi z?-&mTVT1FMG*o~SyWZH}6~GRzt6M#9dt$5*@m$?gi%#|2}D1@rZvhrZqEd(qD@)9IkG+c~c6@wU8$u<&9DJ$oN2A*#He=TjLrX zEo;6ZcYKweG#ZOFyo2A1!2Nuh7?*ntJ8KuY#4l9fwLvk6ylN?G%R}GQSV?UZD1Xd@ z0XB?$r0Mvx=0k`ZAgNzk6|@2yi$x7YKyg{xgGF5n`yNKV?xqLdKbdNn#*Q{j2%g|_ zC#=s-Q+O2w{xuLUj3n~7HHtNOM_5AJrelva1R=}_{>6qw8o!xFJ;paSoU!v8W-}qB z4Ox1CtghH7&CpaUS8>bIon)BBs|IZ_0-YkEuay*imJ>1wX`Orw*)8$6u@wU#EWGgz z(&65cKuD(@a4z>F=wqrEDG{$itj$IYd;Ll9O6m3V$s4e*h}qPS?t<h%%n(bS z?YIHP^v)767mpT{#b>OPYWnY}$1y=P=nX0+X~u0Mse#~)R079v*JpRRoxFU# zgSQDbrL_vi;3wr77HErvLyz9!?QhG{0TTkH9BQsd?ojgh-?&-5Yx@`%EoB2<>}H+# zPuNo0t4lsA^L{9+stePwEn3Ge9;==eCNerneTDAIKL{FWx)O#)hl|TDLA)+hJY+U| z%5?Tufp!8i7E8mk#9Xm9X5TG8o9zVEtKH^C*X{8kB2YDv(!RKZ?tp1dOT}`) zjuoy_qp;X9%W|b^ieTeN>=~^n4Fx!<#QK7TpnTt{^P|(YL9S!mj9S$6+PX$1@^c}x zjb~bx<^^;?+^=pZAh&WRI0A}4KHVl^w&NfA2fZJMBs~~hWQaorSSKG_W|fDqA}HX? zb-kHVoH9;baxf7EDjflgDvOQ)+`t+Jw2krplurndS|vmX_Sf;>#1`Y<6ePk8JQt}f zYQ*#&63F3^Lbiq;b!FJ(UI!HmE*!epEe0+Bp36P4q1?gfs@mVaVdsgcT1$He3PnWn zo{ME~PIzlp@T+FXB){t{-9h0EL%Krm#^LQd`7(Gr?d=3!DFjg{QL^`vurmiX~**{f_|hi zc&fXfrw5LMx)Lx-q_ZdP)PBBr2r(8!OpEVgXO=E}ODId%@{8Nf2JGNjP7>~se;&Wq z4gx{0|)9IWCRMJ=qzLQ@1k~Nv`PGSxRH1 z6qW30sE&C=scZ4%q!$UffmF|cbq4?f%4r9NI1rNSw>z+^5`x;VCzu{3k{c2=YA+IB*BHM*}J@B zPhlxX=}gbpT}kA}_<2sxrC-MHh|KD>IAsW`b_@g|(JFw91huj%dcUfiILH}b`-fG3 zWtSLziSI&e1~XC~Nha@atOAD&Db>4H1Hk&}?s_})Mka%u1 zK2m$~WIO^xR=I3dFH$>KH2b{gQtYN-Jc&u2V8PXsf8RM9uA|6VvMc{V0>&YPkFIZS z9Bu=V!Z);0vY)(zbxE2u*KOGDLig#G91Z`rr&q@jZC1pxOi@-EW0tLE6`8$B42 zE4!X9v+}qs=?B)#2u{xWx-v6lSlV`%41*)_%rUU^e_!;$pZ)Tr4ifVj|rP zKY!M%Lz-BGtN$3?%$~CKle9w+kra=h;;t+(mYkaUK6C%5OOD>U$KU7^e(e+O*W)Gs zd%d6$7Y+U5YUb8QBmBt>X?5Zg4@GIJLZKbVl--EP#N9_`XmhX4xmCZboSFXyzxow8 zyUDrt@EWHrn-(=lFlBYwuweTaesefnfmfmvwr(?*i%J{m^Wn+E`&I`pR4&$Qe~01xO7C!T)))?oNwxu*fB zL?bsRx1SBRcWr@Mf*MzRlix(WwW48E@y1uqOgp6_EeuDEH3zcDp`-}wP6gLNgT}R1 z8J-kH^4=D7bl;sck6`x#cgi_G2CJ_gjisZb2`|*oP~w0J%Ke|~Agw2(*BQm1+Cb*YMNYp4$s?BKUkh!&- zWi1_R(tl{V=4w$xz0>s zc46T?y0N4qb~6F5@6`Uj1OCutDLwa%;8uXvw5#E6%^IaoU?=u=8G@u(b!zcevcrJt z#Tue7RFaU6`bt47O0YatQsW3_5iTc&#GpUPo{zC7 z!D1s^yZD=0qEQNgR`7QUUksk~Bc&n5+|Kh1((XsRWa>#ARWl)`VL%G}FdP!2&W6PL z#r31b*jMpUm8HYd<|J0se+dIU2tOfc1aV7K2z&^um_z>Tw`m?nKL`*;FmAPB7~+6O zzPCtFmRyR-Xoy+)I1nDeG#D!4n0yTO(6s;Wp}}T<$^|Bh%ergUu6LoKLv1!~5p>*q zsB-;OtKCmy%Iu{h-t57l9^c6H4>4hwn^Y33=)ZYbp3DwMX_igJd6YWyPCv*aey+C# zKis)?`Gm^+XHW6$5lm6co)^ZyIuw7;mtd$w2zGy?er>kNw}8MO7Gkx1p9RqKe3%&! zbV|56GTeN9bCjD_cUe0jj6N>t8~igU_)m54t~iPO4oVo73kj@P;kJHqCs*U)#Z0cR zJa<RIU)Ksen6e_s$S%B$J8<4RbD5hXlO@F~FDYH%u*O48l|Jd)q+S*24`Es#ZTz_%hOd(QVv#pc;)%-f8gfm)8k3?swN z<;wimm-(M}M;QjyOPVO0Co<3RpYb{+R>|r7l(eP2`$0al`Y3xvIcU)um=)knT%D8r zI2_MUjFd0xMQE01=PLMahY_|f9~izy z_?OJ#W98xVu3g;eWyzp6&N_Mu9O!sB5QpuW#?_lt0gNk#SW1{>WHHkjBfE@GPd@=| z0u=bs!#;oCB>Pk^8H1~#6j1SeYh1`FlYbNagCUQ|$Bq4HuCssTf_zrdl5}RpyCHRn z;?AC%@kZ0hz%e|(ac;}SOEVIYE}Dy%Zp$LC{X1EuX-RQcfX>|-)V|iS2nWgssGl7I znh!D0ef}$jUQ%By!EWVda5EETI+dtky67aKYL>IG4MZ}$P}0t=v{8KZP${xj2z@cf z(F`Ftve9)^0;fBq^gArk;#bdBbim_y4lGPPoEjhLVmQCb0da%W!ruDhWKTYPaztl02frO#jTUSJ6% zSX{X&JY5-h7Si-kh+cm^55FRmMQ!LpS|SREkk0aPoCo3--J-di%IIr|SAZzyqC>d! zQ&KjNZiecYj%V8O^N^qzC*9r=X^lp|ju*d!i`%5Lf}tjAx5YUCN(%U|w9lLJzG!nC z_4*~A8SnHn-LO8NP07?On~jwTzDiRzfti3}c(_$?x!k|+nP8~dmg;-IUxo6|q+-=g z>JRiSkS!-D=RaCOn8z_b^HCI}vQAfP=xO}hZlj~|SMtd84fNL+kqmn9=kE@V3+Nnxw4 zP-r8zOcUL__9i!WT&3_$%L}uXyK0|$H-^7IUHuyUqz5mBoQhRskE1W4SGI?sSoBqq zfdGU_*gg3n8VO=8D0aM3U}rG)uK# zMeN4k@7+#rJ-vb-Xe_OD410Y!lnnwixze7T4bFr%TJ!Yr>ST0|bs>#99=v32951PZ zv@+j5-*w86FD}8>lya_{sOn}$2=?A#OcXPf-AsFXX67h(%y%RE+ zGddz{%M$w~TUu{C;fdN@n$zmG`=0KDX#D%*CiHS+h%9XIL>34ZvHJ7gMU!N6;H9MR8f(;Acui5=+7Yf1y0>;MxgV^Bd{7#COWuCXzyuEFoTZOuFg|63ZtDeD6QIy_!M9f~ckFxoDU z9Q{@Yy|b{KOg_iA`(&%Nj2$!q97H4_wdzH#Zp>hxEX@)Grs&;u^d?_&3kBjKP#xg*c$CI7vQ)|?+FWM*0 zAHVpxmTFfsn7e&&7Ig*O+9oM>x!|*ym6#|u^My0u2*3@xEW$bj=sYSS4>K=t;Ky~( zv2tMq=9|#)w(|fs`bmvQALZn!2a#&u&tC>4nOJ}lSzD5j_X9I3;<|#Dab&#hWLFz~ zBcpSOu$$@kRzU_nt3}^cdL8fQQBq#L4>wpwmd z`^|jw5ABKUW86%X<7+zk*j4IskPM4WEG4EUnJGV_)*%=ED!n&+HPjjxwfa%s&gPV= z*PACZ6(T}tS+cj|4!yH7R4=f#ll1|S-NQ18$!VdKNaeGKQBB^-%sG|0MZjfINf~fz z)N9{%H{v8dx8l^d=%>IlwHuS20eING1+<|(r5#Jh9wLTwFq4I^EN(q#D&?@lD=e!B z3$rd9{f;sRR~y|266SiaaD|HN<)|BeRo4p@EPOkP)bU2%)1uJg7lIr9o-)f-r`8xj z;+gESU9aXQ71Ps@q2|(7JkO1v(kD$?-~%#%BpdOH+?eyP7S-Qn@=SDqU_9%hg0Dtf zEOcjr?Uuak`!0SF7yk`lYCmjC)7x-xHy_16v({aoghnEapm7RTQ$V3-RBr zxOU${Usj=N{-GSetYgt&zolaq#QLpUefbVq~lx8wZbRbypvx7I9eud2F5^r&lV zy@73oENbFLVvSL6jvTvtl~74n2)E;*=w3_l;X8IAYLrY6pGE!@(g!kkdel*=ya+kf zE-LgknBfU9OZHT89IILWb+_q#I0-VI=*`GUn-MwsRHM&4YlU8u&Ag?c#bq$`U7)W;Pv?luqt0 z!Y{Fn--tb3l{Lo&w)VA5nkrDtXP#_LJdv}lv(7+|IUHOZt#^w>8u7Lq#szfj+oB#B zql*Jmiv+VL04xpum<8_4TO|dlb#vXaxhal9-&u3vIC!<<79k6{;x@WA>PcBdmPnyS z$C62?Mw(K@O=p$LtQ%UA3|V?8|XQ(A=NEQEm$8D%(P$nq=;ck<^7_E!%7`pLaW-u zygzq^9}%|Z)1i$qMALs(f(jyyRd&fAihLJx$G9HHv&F*^6l}q_7w3abNwPeZB_iEo0TGG)SX#Q{uyeB z>~hf7XIacMs7Jhi9cpC^!)sV#vk6)B7c!k1dKeBIPOn`!9xv; zHvw#{tujr!!PXH<^Q)R)Y3I6fgnd5nH!+9o(|SB>=WdcKe#0cq~tt zC&PT7+%F+PIERWHnVQDOP2F(AM}_a)dwx*yZ*2NJqF6Mm(w_7X&Tc%h4~@muhe6g`G_Z_)TOCRX(`)t>kV-WlImfXH1(7g?F-$P~bHJyYvs zFSF|1(`j!q$~Y?Uu@P{^S+Cj0p{nstm1xx|Zm)QE@kaY{_IeYk^)PI|vYA4Y&-XA; z&%f|W2;<^0Gja%gV*)(&i5_?X+n1Ni9*Mm`P2O8W!xcB-Op8kf)10mQ!Ima&@z#lTQOy%y9l)93yOfq44);+rHX2^kqlH(5fyo_5d_cKIK9sKr z=9RG0q-Bky@1hncxeo2a5bt&*9qH#co}n>fj4*GEhuk=*+8mcJ=ry&YWZ!AU>E`TX zz7=>e%!zbEPF>^&=&Zd^O$&24-2DiX6Nqz%v*lu3*2zTNH(AyW3a3b;4j|c+suC-~ zjOUX02qvUc7W2UW#OGQK!?;oyLwhc+nN-Cy)`oMAfxq!->U?w-7f_a{&F=WfSAijk z93IWh&V4Urv}i9Qk~=ThGGAy7MSC?s&2-+#uq}^tf?cPKgd|Mea9-p~ifiw#*`930 zgK|W;1A7(Xd&V`GFh-)fKwYjFdb%uW9}s6uX^+p~9NRBT5jJ1M2C-N|5K2=D{T*wwHcKeG)sFNZGuU=(EOG{lWJNOqer zk#Qjf;;~cU)@AlD4$-{{J?-&~`1YMDLqj(Inf327;oq_CiZ5{m;M$=FXvYD`k#SQI ztS&Df;wID$dEucbYwxKJ0Y-Q|nc>#h@JFCbzYrL*K?)?vFx9K?0rsNLvBP&seg0d7 z`H`=>HG!z18-tC9@dZTuWXShsnb@j|`zs5&lQTIY!l$qx9*~X^E4yTasM;CooAA*P z!1eYIrTDW0Zb|65FrdvHD!l><@cj^O@xUTL^^E^rc+DW1d@|l2uK_oFhjGPjwMRZ37&M=-{B|+@gjTZ~H zW11&s1^u;9c^YGQvwYAdTNZruIfZ|st|&*k2Wlqc{L%`Wh{gSykNPF^U1hGTKDo#e zJ{;=nt5{ifc&fL`_FK#tjK$y)m2mWZo?NS1P`~tPcM6YE>l*dzH=@X;UOx=RpU>rFnUoSY%j26NSyNit@j_*M}pyBK)Qo2Ij+3CgM0 zGWg;pf|E_*yZNfMe<2*VH~0L5NJd|R;+0$mZVJlog{0TsVWcYW4zb1MoTlrdbz|6U z53#TF?OZ3EWfXmwh|OPkgwyA24tKk~IU8KDM-oo_DU%o5)C$pjLiS9l#lPEI^&U#5 zwf@kh+CuuA%P+U=d`nh2Tw*f)vcA=$>9}_|4Pk+lGX~y|J1W&O*91KwG|4vP{O7YnG*W`pE}S44Dr? zIloksvJ7cWhND`Nkv4<{Wgv>mAoUYRZ-AneKoX4Duv&;dK3OK!@y$sY@(PMWlzp9z zR{E8+>_3=quOD?)akWvEkQf#RF%2}3MjYiYI=C&nhLa^8GG=ag_^zQhhl9@ng`Ow9 zmmb=McP*X^qE{l&4T|w$)PwY~18qj^ODuT36zB7UHpYwST-N>}jhS2$&aA$FBin!bO=Zk}8gpVBScDIAdx?K5 zU)U-Sk%E7wio-Tu;bhc0vBdN5JJritt2ICL$eIovng}W-Wy*5YL^-yban6X#9ZFOFZ zfWg~>QS#&BJraQ8bQo~fXaoU#xW)z9a{Cxsb{(fU$2IXrOb0uMC=08!ylvBYb-rZr zR)I#|*fzfeK2UXqcmB@*b-u^?oKhZ(hh&KANL-pOA}d4+vIBb(x$bgNuD?|`Gp)i0 zb22gu5H}Z=k}9o=t_iL>Y9(vgGHKZ}9<*&rR@z4(<}b`rbWeOq1OP{^w}-=VS6Jc1 zcChaxY2KgUPVd;O9NRsfiGD;=_z4?(W-t5={WD1p+^zGEZ7^W+lWr#GJHD{msOMc_ z#fd3=b5zK<_>5*cNOj+TT>|knek8k_SqZORB1yJaLnmE7-H8Wt5R?{K{%;F{oq%mN z1pAKb*t_)_{Ldkp79az+9&VO~L=|RKVwZZn)r5~Dy^$IZ5&O<{>JM0;32Cvul`pcR zq3_Bg$C2O3@eK#pb)&!FvjL;V;tkn*FhZ~)n^*5OV+ZNm&}oCBd8 z1tgkV^g+uO0u=+1vqYi75sJLNrz2cuX}Im5#d$vbEeeK>-D;xM@|8_fGQwBKb%1bebhhC3MX@GUIg&?F^|y<-=!Pqsol$ zO>UbT1ljbJCh$$xdQa@FzI+Ug?6tn%we|na=4Bv!=CH|1gctkT?p6)@B9?h{?r3t*aBaLS)L@Qn5Hh{6-T=Y-9BhqTl#zD=ve?MH?uQ`8jIi z-+SK^KlD~$mc$Rt=tSneg8}T7aUsrU$ek^r!=0?lq*uh7{h%6JjelFOU{`jJja%wb zs~ShrVj+K)!hRECApfN&S}?5sy8dDS@HhIitAh#i%%ND zk0U>a)$wjb#A2&(nx=d=C;neuUK{J5z7|_zJ2;O+g2B?iZMc=g)P=%k$?h_?8s}4p zQtFIDT0$QB$g=%6)mW!T&($4 zFx*+LHV8`m5NzEK#0ez++Y4o?<<#&esOJyNXntF0qgRh#&1NsIfWZFK%cCt|jlSWP z&|+S*b`#k48ul1ABoLpih#49b`;;JAQqjxhheaRfhAnwEv^4}Xu#2%Ly4-rN8E}2? zbI~KwBb)=b6HWMNWj+kr;N!`5nQyHb^bx{vKCYEnZg1wJd8l&;AhfqEb4S}QQJ{o~ zEuP$IeyY-pvuA;)U`$mati1?`8e*QjH%hgZjo99Jzb|8!aWP&u!uOz*`QN!SyUPZI zyJe3)@0*}23{*3M!JMH>t>1uiHs2b?<02cDz|uCx) ztc3nGYG|{7e*~Bwgw9(9qmAn;o;!Noe8;PQjuy6r2lNNl*TUv)+8BG|>}!o(>T0(i zr{t(0I30m2O~)mlrLU@Z(|B#FMTRJ#S>o=*Q1q3#kBE5RaHZQ6#5Hu&WB`db>Kn?Y z#^D<)78~Y2bwX*M=42VYR zwUlEc$>aK^kJKRu#5{wh~Y2L%lp=7nXsq)aKZ>_$Lai}e!yn&vZ88zb$+ z{{G`m&$qoQX$w&csT2FbDRXaoG2)PD`k!oP6eXt1f=V%U zq_n}Q#Kt#ji(pmx@vGuX%(s8x3Gn@h;8}vmg_8%ZYS1^EZB|}psT{yoIg37*ZGp6{ z7-S~T#+ptoqiZ27%SY$XB{5gkgl4hH({k`-wf)yBps05-(@Yt==Niaw{RARW?8HV~ zSXQJ@Y-d`Z=UH%)g7Il6Cf?_+*HkcKn}uvllC zKqGOtMDBiHNd6ch1|!mUDdcT3g>TXfT^_v={ImzmlZW0kRWWwCre^&bDS23(4X_CT zbghKNP44Go9gO`9!A?v>+z+VL*IHE2f?=@N-_>2zlaSIC8cp@7H6AV9=MLq_yF=Ok za;jl$A?fMuzQy|*I;g)k;ZDfw5MO~EZRVI7+Kg2$Mn~8@2apFh89N*0(d5^P7n@C! zz$PvGfeP78mS^d23{FA!XRjX$UDF9bx%oKD+O2pMtVaZFA3T}Zjx{k z=0i98^P4dRRPPwH&jz)pKS(?D3<|JS>bQJ0@ZPPSOi0s2)|HEQhr*lc?8A) z0D8O2I^T*FU22P7i%r0C1L?gG^IDp-?mFlh#EduizNFNIK)Q{b6ubJXbHlxv7E3ww zYb=(+Y9mt)4zgaIPB4()L$lsd8P7l1d~6pkwFKo%5OA@C1Wkc=;y!cs8Qc9JdYDw8)a>F8FTDI{IhX}>mYG{=T44|nnJpAX9ZDtNm~Z?0htlEyP~(~^$OKYNc6 zJwN>DM-xs0yatM2IdpKpX-u3(cw9CPxI?MO-ONp>R`Ur&?M3Cd%Nn?`F`@x%Ua)z= zVl1s2cE^u6{AjUyaYbhh?UpVv@PJm=EOHlw7 z9VO~hK1b$8ve+#kOIe3(8pXIByE0OHqTw#WiGlHgSadM7p2$`V&c#Wr)7V{d2C>Ew zv!9Y^^8Vyg8L{F9@?;BN)A>h)fQ8Ew6%lyBkg1YCVk8m|?B{!3*lcV^!E-wPj3p%z z<8Hc-k%gEVHPTVU-QOvF~E%DO;sUISVZeza{cqH}0cb~aEXiV3| zICXg#sc@AxoM8BRtx)`eO5KqzKLleVWRR2ND+S8=HwtO=$_exE%Bwn+tPVkcE?u=? zXMoAFrJIzTxX^G~$vv+qs|s6>p4b_H+C^<>eZfJY8OS5^CFDH{wI;0SE3}zG%&q6z zh(u9(sP*?d?P#kWTv}chA{bN2Q@ng|b-~~!VmZZnzEI*rR^2|V>OZ3N4af1#MtiYMH zIe2+gU>7O8+@d&$?U~W(e=K-AIhT!J&D}kN2EP3#^zV-fv`BD&*lP;PnLUlIcRzj# z+|VzReE5W%3h-a}QyUs69@z-^H?W&a!-{(vi~Jebo@<~TnIG!+X37sO>ow;?j>;Pr z-S6rQ)FDA@d`)+++J+xr_~|1|Q8~BAa}?X(qrFU=_iw@3wDA4)bI8I91P&^13>g(Y)2W!yS|Vw>{EKG3-!xWT`v8kxg%5~d zJHljnK2er+bar+F8ESl*>hxl|I=7#|P&(VSk3V@9Uu%$SP~w&*F=1TU{Xu4O{NAw~ zMI@GrGs8l5YfRMS{MBLOwYNeGxVFfB#i!;URzTBj+Ljf`L~tH?%v}_D1XYx$KNBe{ zX5YncZh?&m8DE^Hzs?%Ju>_Z}S9DyISta@|?64AV;`QBD7tXxoO4>jxA)P-N7M6b2 z97aN!^*yNW8xBR?>7E7vm;=-o_8u~Qblo>nf#JYjk4r-MCSmI;u?)jX-&AlsL zHbdXaWliU!h^cF#yL7ryq^+~T*=o@*n%Gb-=wD9x5|3tQOvp~2@3}zhsDiq%m&h$9 z-G#%dn-59Opjx%2xm$+!V=NFd-Tg;w)AK2T@4SnK6(6FUamrP8mVPIGeTQlTFyopo z_6uK`y2>2-%4Vk1mC?asgGgmq;xJxVsZ-G*gNvX4+!|X*jY@!=ABkb zvhBfx`W0CToYNr(qTr$%d?keM2&gNzqrnQhml1Upt38#K*rw5)kd^@{;^(D1W1KG+Q+gt#Qx@LFl`lI39SkR7>l3CoKI;@LEb;)m|F%t;*-U12Vl#QWE{V z-1h|37D?4k6iI@29C$=kNG^Kq;{1R|I+2fQ&;yEWRT8yL?3w-UWus+9Wy-F2f$<(QF}*1pWbM` zYp}{g!mu=?Hr=;z70EgKQ146h?#rkP!7XaO2{$uzt=KGiAg=0 zuF~OE2=a0%UQh6w(3S!Iaa?n~prM*QX`_95m!KZUgL!Gb!0_kvPvQb5=qSI@o2s5{ z|M)$VFCIpSo4U z!?`2kB)P{zX0BLN+oQ{+aLISnXV8;-?qi@bkI}iFa>o36m*xw*wkzjj5=W`WiRCZl z)m%>fSpPys_)xE2O}U1ida<`B`rvCa8k+<%ggSXUl`E+Mdj_w9fe=$op>5qVz&%S# zI}LO1Dg-uG`B}afu~GqtvxR+m1`*iOH36M0uY|YrC&}XFle^}|#lpa}rLH63u2X40 z+yRFg3*m$LTfOEPUjKBUi{%CZ7>{Wx^8W>16KkHxPFSk_-I`E* zg(!yO+-?MsNYKWc`wA2mrhX3J^E)_FxNAPzx+{Iutv2DZ8=4CliMB2oM_;4z3$Rob zy0RQu_ai}{S^Aho6hW%G>LHkja78}kQiw+=t-)wYoTXC;YRy>HF^3<)4NiQrpQItJ z$AXU@c2`{8Lt9f3f6Px3fFK=VwildDyB{D@a^Jh23k+hJikIzPY57ugv2SepE%*yh zn2Qlj#3u3HI9)d50}Gu9n+2Gu?pCc8|2+!r>vjaX>0By{V$bfqJY zg*uMB9s9mBP-#r#H0}otxjTyNOoAq}(xKe+-FP3)2LOKxKtpQYn#gl%%(w2;D5O4S zJ4J7ynbSlt?pIM_>PgAAAqZI|KMucADKZ~QGBW`GPh`RPJ$42C&0o0m`it#>prPy4 zE8NA$?d5;ab-~o3EKk^e>&hjn-K4m*#rL&lXU!K*`veYyE+l5X+J8ZkDjRUjZgLc| z#}M}HVPHb3?zGJDy-@FRFv5H!Qep%kpNLX zebCge2qLZfKOS4nC+hqPWimEAX90Z9_1we8qzAfT6L8YSrTO$80UCcTy%-J02Q7eYB3jg3z(+cncd zRVdd+{~FMBlrFXz5Y^wpUdijUKahmV~=IHP>^j`HOg{1t^ z3Zzww*dQ$__vJ>6(=Nlt>Hx8`r$qjkX7>HSxAq#Lz4VZBCqWTj^=QP?a`C~Gg4`_|$p`|h&V1iXMNruP4)mE;eG#GLdQ1LbWJ zy{TFtlA;k>A2hZNff6^<{QdaV?sq_^ZIZygYI|bqUtZl=ER#52E6?J71j}2cAl5pL z+poL%RAlgf?ayI2)gc|65pkh}$#k%;qQJhL=oJZl+;V~-(Q8#ituWRSb!I!_F|;TX z`9);a3({buu`&wFFM%2dBw%A=e)<15?D(bC1sI zf}D!eYbHGZgy4k#?e};cb;)00Q$^*PP$r_@jOV^kLu3rS zGI+EUX~jP5ARL4^ep*s6#M*^d9WT_~Ay1Jpb3R=1F}{}cXI`p9I&Fc!W(;G{Dqn$h z&|9zmAp>l#TtFZ|Jq2|^#~3f^&I`Et+1<~UYa`(ZyoAM3)E5CkARC3$(;-pX268_5 zwS$%Rmb%rjVe={Qm3tqEcjg@9@v(E-{TB8SLMg(#pW(hapEzw^Fo@^HIu0LJBOQ-i znKx|?ndj5V4O;q>?lUJK^9~QOoh$Gv71nA;At3iEjsYH%CH7*vz2*)R`*{xx02RB}G@T}ob94zRN! zwvH*y-u>!;Xf z!(`(7fKn%-2VlF&;hgQ-JQT5uoL;WI@v~=A^ckBhG?Nv$OMB8rLK*oEu%e?Op<}E1 zr%G#ojR8F0yI*wfE%N1fXq8q#0;J_`$^huYA0)Ad#%nPN(heT5{c=gBhyk735+e)} zETOqDe~(bcyJ*Y9tg!xBu1B7Da0^eUH}y}7%NBKJmA6&$`)fZRFx|ku#l(fFsE|KA ziwh7qa}{T*@%d6LXKi#RiP&A2M;5O%*W+8YgTg^T1IiMzIsDR&{sUn9k6fcjm{8-5 zrYz98D|YJu&W}(>3bjOb+cEgCsmHai1Pn8IJ21BOoB0>nGX`H-N)PG5PWI0CsGr}j z`TAjhZI^pL^fPE{!U;=(=uf$L_fHW+A~e0ct^H;&D9vFN;_0+_U7kpkcfA~(+x!cWoVJR~_ANqrEy+OSogYlAPLIZ0?p=Cc2`C^RDa4I?W z{!>DWxM?e8LtDF*RzKTQNYxllApqP$jD*;jK^1!1S@v{J{O^{}xKPgv$~DJP5<;Mj zM$=!Po4l;c!b1LR#bsBcw7qPke z5ag`T2~FBQ$=8PTX{K`Y(r*mSYZGkJiRN4ERg+y&t0QM)F;?hSTl9;KLVdmP^YV_R;h{y^_wJV{ z!cf0^g*SUW^pZ9K+f~il2EFF8hF^&UyJl0r^nc_(4ATicZR_14b#J6;G(=%*i#_9T zJHimR#~K`UQfGi|%sQI_1X&#Hjh?z#yfaea_nUG%p2f-lAx|J$w-~*jXar#^T5F`z z*`VSSwxvb~ztbXloLzWeEs5;mBrCK*m?Y~uj3ZGE+yB7kW=cD!0&C^+sx08luT(v3 zgi0W$okRn#$PWX(_fpET!+A*P*9aF8O!mT>>tDV(0~%u2`g`F{3iOHK6jg@IG`qYj zduwzn@q_6(z;?rVB(lAs;5j1yt?vd=1RgF}#BcnB6vL^iA=3;m+h%^6_0=^SM zlte-XuK97|F)rW2P=94Qbqsl78jnk~M0dFySW-(P8# zsj}dPH4a6UXz6=oSTx=IuqGQ&;`U6Of|y(RP})zEs*1cFqIqR@%GQNlIy1Q!EWtos zsl4lrhr^GW)*x*ab8ouBzol1k5QiSIa1@-&H6-=7S616-dFhu~g*Yi*n$>q}dSn43 zbA>9K!7Z7=m`eJ$4}Y%Q`>|(eL{zR)L$`L~Y5j-_4Z4(!7{g_`{(5mJq9Jle${*1B zy4%7Fwb}&6e%g{OPa?~b>PHb~1BZfxf`^Q^IOvlk=QjvLn-g2`Awh;N{eTY2bT^q` z6GP-j?0qf1^`FYY+VdC8pTD~;HWPNRQ6K4gnbj6O z+7`(Ns%<6ghT<4s1Kvs?{K*?CFk8u|LP$ew15jQ6=5UK;4pSqtgCF$HZp{fq*Ue+s zLS9#%WNMn8Z3}>3?0*Kq-a`BRUa$z0EIu7y;i*LkK#PGl8SKsOHlPXzv!WTls@m)Ud~hN{pD_!idXsHS)8g1^&0W4d!c^h0ufyZ#HCJDZnX0@_Y=c8c#3`HE zs2#=Wwz%}^pLc<97LN#7Q~S}e*n@rG`#PJ3j@A@rrhQ#gHVZ3KE7FG_4z))BWR6EE z&4*}XO~Q}(q>A9^NsYN!n2b;HGm|H^G`hNqW_jaOVj@usi6HFRbj|GY-B{gG9h{mS zzSTKIY_b16LEb>&)#bu)1_IvZ>!`*27&WYZkSjAe0m6xO$>V}KH#HL+!R9z+zv6i^ zf_YA7N4`bh34l}U^Uu1lk*iZU*ZlOV`-ES$!B;vhYk~g}d*L$@V&HPL`!z-%Ly&tZ zNM8IP5oHxctN@+{xTtvm+WuTEHuE?OM!r)2L`~B`(`3qf3$ZrWr?N?HI_h+yxLMLs zd#e_QR=kz4qeN_g&>QpiYgl}AjQHK=h49%546r8oEYl7+6FseTaGoVmpdBY0yS8Wg zSXcQVZ=(CIhP0l@8PC+m=O4%aru1nMUY{cF_hv5a=%fo>d~tb$vUdHB?F+VFT-o#U zS?M?okhOFbnWN^%4$;!cdagLs5 z^8r)S{1%_);mK1efs!8Z{vQ0gW4yD&kYL$a!1(69D9$0y_^~)2FGmI!SduQKk;^mLCR_f28MN#L`3d0ZXZjX?ewTUQbxwgIa#8=B zF+^|nA-R3s$zW)c$gQtTo9y=wWPg}TRynb<<3 zD7EYwcOO}K2$kqyi-m)*7u?MA6t0h;f5-!`H>5`UtSVRO9a{U-ZCD4)`hrW8@~?V^ zy!CQ+wZS!@^y*=>ES&pVF>oZRw zT`LmKTj3O^-yQ@Fr+#8Np-fztd`kE0LwjKmsJ;{P!)vK=8!3`9C5qgH=kPw|o)M zRZ=865b>w%idza8h!vG)-EMpMyhQO2NI zp(&zyUfRYwveG=H1FQAb$O#1al_&D&h>;Wgf2AeZLVC9?l zEzNUg4NciF5X>o(K!VS5lYyf$D2A>Qs$vRH)C8ZeZ%cI|7%So!GGg3U&JbOg;8LsV zV2X~JMA#=D*Ce%|f94}v-SrsJLJ=z9D&G4Q`(HvBPMC+3o*ZUdm6QFk5Q35QzbG1I zp^Ts+Bnx^G`ESVdBRUzj=uq^4dAp%P@9Njkq`r-40R6Kkugemp<+f;R4N zqVD@&5~DfRkmMxEg&4#%a-!&=&SL+#AuDAkTBRs##P?|H62G*=MVp7BUhN2=ZK#nN z2X;xp%sFh-{K2aoGJe zt*E?4oz57pwRq~ybj>wS3uew@`sE`|BCgjpLg<}97joFi^cknTyoH|&aVzrwiNn%q zn*^DU<;J|cS&ZcV<{}YD@l6wgc!d3DGzA9fe1TRWghW0uPkLBF0dFw-arGGacvC^* zcdPK_@&$v1@tA&Q%X}DrPe;91nQrHCCZD`F;T<3}t2|D(A^Cj3TRVs?tf>Nz4OLQJ z?x+NQ=}Acc{KF|nUfIo*a^!}y8kzn12C(s@wXDt1O-{!LbDHup8u z^DAakbIX(MLKZQ7#7=2s7gYGBN1Iy6N0JUJ^tqVF*ebzon0}DW-kwEP`Alyi#~#FI zZpG4oZTclK#OSy5e}in{m4;Ou>P!|!m9U=j3 z5~P^+fx5avHa(PSkA_*9jU_Y^+j#n?mKshqm0i5}`zc0t48_l3{1)+U2U9+{pBPD9 zCr8cg#{N5E0h4Q1zsrHZiIM zg3c{*<=&uuY)f9aoFa>o59>YO=ljReuY+JNH*APA>kD#J)3HFQ9Wnj(O6uN+?159i zlCY3b^zqqqSNC}gZ;!Mmg+)zxpF#(LFvNAL=w3#e?lX6s0`-CT7TL#BGYs|}+G;jI zUeWJ?1wY9_%&Q*%(}jg0)xfP@7jG zbDRskoog>T-tD=_|wcDHKR9Z#0>$BSm+VdF6N9 zCZnmpqU1oS^8~h0x(FX}|GSX)@H~kIxU0hMDx|p_&MmwLo|%@xomvhx*zJbZ&kA7f zY&af{3E9rO{G9{mx@Mp8tX?N#ONY|i5?t?Ed|SHNzjGP0xk<8SnZ)JM{Q&PLGyr?L zX?Ti0H;f^n`ta;-_Hf+ntle2jE;W0$W}omsaoKk%CkZ{FBf>IeA~wUYu>Vzw6{+{rn7YBq-(B}!!h z`JPel6AJV`yvz^G(~*I|7mJDev?KZI+%nkn^huc z+@kK-xg%^Y)DT(Y1V>&;^f4J(CA}=qr$!UDDv?sikYuxZpz!XY3~mHufqj#`s-=p) zyPZ2z&9a9@0GZT8z}A`&r0Sjdz1#oMm5gq`g*lNq7lfr91HfibL!oPHP0LB_OVPFy zp6)B6xwgH|=jA#$>7d!zX9OoLXP4U~MBo6%t~TI>xcmbQXe*<oIc0r+HPyw`aGB$j`Ku zcv|`792jUN@oR{Brpa|G5p#AsZ?dG)xzp$bF2D(YARm{FlaF_W5zCI&Dv&;;6=%K% z8DH}=vM%`_lY5JiIfHWcs6RUaxyGK@cYLm#+F+-!_b_(+xI1YO$OHry;1KV2t3IS` z5v`3IOjWr*5WTl;3;^ZCd*n2NZqF;bCw6A4MPp|DVU^uQhl&2dc5qFBffWrqw*M7R z^s4h~Q891#iBRQ--yf{Ss9EF!Se*&#OU`zuoquy*pm)GJITFP5x!4Rx0G{#1D+xkSPO%_8ItD`NaBCg6>o1YxIp6`$CXi>o=#ITtgeC9jG8EFP&cMKprN7p`I5tY@!y< z9_)pUw!VoxlCZ>tB0~@id>n+yD(}F72D9Fub`YY`HNG50n$K^0d&wi4Ap`=9oOa>> z_||ogq2ilI1^io&$g6(3pQXfmVM%I0RAQ@!5Rt<{9?NaxY}eh#*=;ec)h7&gj*;)g znb^J9wkaA$F=VI~?2#H&jz$}q@5^hJg~_O^WgsTWWi}6C!N7&h^z6SQH^qz6k{05< zgrKmxj=QkCvUoan6IhKDv4LUNti(|Xo3&+g!aTO0=)mSq=5EWL3l@n1OO^APJhjCI zDVz;d{bU&Pxa{`qw?O#}*9f@?zg{^Lg&LO0+?kQV=_Lx}D)b()-an062Z8eWCEb?4 zK}@(yp~trxjb#I+?ceKOYSMZ*bOs4npIm;8HuG))P*BAR) zp}evqeU(^kgUJ?&(P>kMZLntLxr_E~2P%8?Z_)FUCEX=}Xd%(c+FbQej#F&^oliF# znAWut(ir~^(vPf<68OTLOjxTZ$KVQpP68k&k;dheI^cgKrUD`}oC~N;c(kYR;L@H_ zRg*fJM1Vc^9ixDv*H50Hc4I34K1aKySkYifT&;NxQK@k2!WXhCu1q*7n{bOL$#H=t z8VGCadFfipK89f;>;FBI*d={)`8rv$cZsW4dC$2Wxjd|g)g=<6`*3O0jtg$rI7%kA zB_T+7UqpD<8O&9^D;zT_#=_2T!Cif(?7liHP+te_CrBjuA2EhCA5rT+pML%M-@~xrBq{N1u5M~WpfbgQGa+fvb~t(<^h2^b2x@5j(Qh=- z5XxXT#pXqndjmRo6UWuGd^WR6H2tV60*0RfSnfyCM<4YxmvcUJ*X@T?AMHaegXL^~ zu_yl-ysWs1@jih)DvWVJo;H^`wgXV>ZR`>CaN%$hM_eOxqxmL7=IXS$o?pDM{voTG zblD-7CC4#fWHy|=y!$b)jYW`o*0C6!`8MP3IsBnfc&t6Mbq8UCug&mH)Gzr;ZCzW6 zz`%6l_c{ED=v4xG^EBSa58t%SAFM~n5V2Qe4qgl}P7nK{_lX^it0TC@pJIMYR?tv! zLU#FDmYp}!cPV{a5I$>e_|dVM&9CP|#-qfH9f1m7wUOQYF+~bSz`yt`yR>En2hBI* zAlJ71%shYkU@!oBL2gQ~_HTV=OCtStj<@-{I#kO)+^FM4Ej>}t;GC@Xrk8vk3F4=d+jsMl}OcH>HE; zr{0%57i^)}Y4~{6vLV4;ZfyRPI4Cwg8(P?3xyHXhYKSj+osYf}0b8j2lq#RrhV;|s zJAszBKO%k7>N|D{-)m7cQg{4n27H#bgB#{DMA&rTiqs>@jexj}3CZgM_(a)CLe?A~ zaSGmqHaYl_VrX|KPmMjkTdO?Uv3w1;Db0IlE>T?FHQRx}&@{mJtd0XS`K!o_LY$Jn zppy3u*G*M>j+S?&9r1MIKr(3vn^35cv4?jckBX)x1-!-VP5AvuLeM@>`@7AZk$qi) z`8fK#PJCa2+s!T2;Ij0reU%JLv*SS2jPttVt&GGGBlC@oQqMMnpUN}&sg*X|P!u1# z7(CCENnBa(!hRfIpWHSJV*neaDx;z>wh z`_ydlV`W69#)G}}8Kp<9sixk8 zzpL%!P>pbj2OtAbBp@VfgVW1YSwBTt;Fl$$fz@VZ zH`Y(P5vm^myb!-ge2ms1Sr{h35e%&)3uRk<#J8MU+pTPVQe9)mm)nWii+4{ey96D2 z*JGHxqh_2T#3p=cgo{`iVhbIN(>s+BX>SX(BoA*Wi>30}v|xOOQ@xBK_UHR6Qr+FU^W<(m@~ zmA^x;OzR`xXaOt_-$uIm5RnO}*#l|SSdev*b`G3gwwU9z`l zZQ;#E0in&k{5qEmSk*c!yQTJtR@dmJopn zlU~|967J=ym-R(gPczVf&?Lk>55iCm6kL{u9h_y7fda4BaX`GM>-lncKT=Q=qR#C0 zC~ryDipEvr&=PMu9?I>`^t-DL#UMh1o=rNzi>G&*Aku~Kb@{=qm|5L#4Vl4?2V~{j z*l%`tcvH2jT?dd2_0?X>B26c7RGcQ{Pz=2J1~^aY8e=Of`D>jAjmp_|Je{AWfd1!! zo95|f4gRLFDZ4J`k8@F8{?Sq{za~>$U0ZuOyskovQE2|-LTaZ_=}A}Ij|O@NDn<_x z_82znlVm+va2w@v!;m_&ls_S&Z%F=Np47!@4AxQgdRJujJ(Y^xwx1}uqMe4&J=4Q| z{(;B!5Jp8R^Y_WEwcD57CVzNNGA7!S6P>jLW^kcZbjuX^G3_yOFUPh%DyL$hsxfs) zG`N|4E+uf`m;s;SioTu?@uIQ*(X;E(t0TS-&dLlPB~4JtebGQ<)I_EcEm$oLp@01; z`12+Kf(jK_Gqt-Gw`vv})}+2}AHdK6N-{b1l=r?KtQu}|@WF3WHan4MV9c8KG7#sbU63H7wBSzJfdLg+upf8D61!KW*n>N^y6rQeZ3p&;Gm_*D_ zVK1PLMDA#av@f%Yuu&D-k5m1gaQ~5dK-hP_C+IM563iy27wb;7Krai$-SKQhJgrG&emOiRS0S3cV;KA0{SS=M&p)qoh>vW7si^Arui6EqCDdop@Fsz*Si6#?yH=iXSP3t& zZEMl{dXx?dZN9X_JgEw}Juw-RGkp;)>HG=L#I z*RiKQw>%FvI0S7K-2X?uzxI&6`XbY?&<^st%?w*d-`J0C2_8ct z?dx}rr;1lfd8x1bYAdgMEoQQ{U-m8E7hgAAs$7Bqe3m3K#!=l*cpD1O#Sk4BVyrBP z2E%oX(fXJ^ie+ehV*EZtW0lRB#V_st)1sfw{!qdsD?$b7(sQ|(>gsaWShuc;l%l!; zU^T_Y_$uv~D3ITY-T`<1w%wSg*26vfC`tLv>dw@cx1j-E{s%;wBfjep`zm_L?Y!R6 z!O9@16;1gtO@BNuM;@Ez(tHSWSxpykWqq(#gY=+WB67vUZy!Al)rAvoEER`K8JR4{ zUb}kXg$r%G-Rx`bQf%yw{lc16vP|1;cmnOZn-vhvmCmTP?z^^6oRhqU-jza(fu)Cz z5Fty4)G$`kTT4{A-}CkaP}wAUnStXbX}X~Nfg zVtFnAPy1Q#4%&E0PUs{+)iQlPbxBgY$I16WQP4SK!XD^HfBDOu3XG8ts{U5u;3Nc~ z^(vG0R5qAu)2%kEVGftg2enq^UT8S^ga`TT?^>5aZz}~W-dpU=y+01>U3DrSSu3^$ z(*L?$zf94nWe8UEmI#L?ah}Z#q`!&BCKEih`0IMC=ipBezUoj4$C#NM=0f=({L$_) ziAR$~(KU49d>;CuT&2W_U+4Ge8Hsv_65odgCbs`m?xpMAKNKUm_Lj#cp6vTAEyzl) zp1F#n=+uw$@U#AOYW0gx0~B-+5?#%EZnuB)?}u>y%b`dytn|J&p-AD)KV{Ys$a50) zzt7W$a_MeSWkSB~Mi;vY{9SJN4~jZJ|b zKcca=a>ojCQ%Fsg`1w8l%m1f>yLEL`Z|cD^=$Z(nSOPZ9xhd}28gbDTvqY=LjzI<= z8NRNnuBem!Le@_k4J+ShG2vB(xS<*b+3y*Ef}P`ga=;ZEPU+R;FSD0+PKO(1Trfi%uyO3$b@yyc~89T#WZ45GtZplqH zh*oyDsQ#0@*@ic@>dE4cbzgiXk*hb=h->(Zt2X7tC{C=XJzrrjPo;cq9-Go$|fw&06_c)MI)mS-Pj4?mGPSE|UD!(&2{ zk!NX>Udawo)%H~hj?7Q~Qe3^~nt#K&1;{5@mRR9PwUN3OLd#{&deDyCst&SaxhS>5 zumPJ!ncHn7ws4Bdm`lFHxA^F_QAgiQz$F6L-#?-tSGdTTS= zV%dgDof&yXTn(?O6{fvk4;0G-z&&W>#5oR+>FH5%%4WS`%f;^dxqck2m8ya?E3YAI zGW-{yUVO&I=tM*e;)l@s-Q1+C9xDVpTYel9yvyl;?48C0u?q~VLwf1tJ>%*GM9#4 zb<-x(bZube$03p@&NeKeg|W(o!LubK<=;FhNM%6 zBqvN`mez^`YY&ZCPd24c3X@&M(Ne;}^1p=eV%%N*HL>ab&@ZOF>i-aC-7R3y zg}k9*3qmSSZ}EPOc1lLM#+YY+Ec2(ahb~+w_6XPWQ=5ene82@UipXo1y9{IagAJ*H z#)0_5D3QuUaDMFj#M4eQy7x(pI(5q6!!Lm3Cr1eAF`E&{J%gHAsa2?8~R(6+F0npM8$uHDcb+fgZ>F zY&}gHPM?`PGOxY?JRZ>>Mt{=PPe#jb zE=D!rx|^SL7c_Aj=}aG9SP17^zyH~;gm*zA#%aA`=WWxC#D7jfhLs@&${JZ52Bl4F z`5@D{7cp5Wv&tjd+F9Vv*TbF+;Lkh5ttv;nQvpZenf+Bjz3)Ab^)KTB)^~=NNjr}{ zxA2ncmTA4H%=$TV$$y~Gq4}l>U1M(v`#kXb$nd0UyU{}$oIyIz&^VF;mSOo0oe#!d z9YtWi5Kvat7{ZN5&7~cgkKpIn(NWm%OFTLn@QC6upOjGQ-wZk2&=Te~Z%&_!7G~eix zj#txd)Luz$AjV_`2686?Jfg1CPVC&P>>+D(f&B~k{0iRW&3-rbbKl1{`LX+dCa?L0 zxH`bSbJ^kA$M4z!1P?-s3(B`BE~1%y3pd_7DZr_-l>%Rssy%PdTVTS;md>reIMR^P z5P!{W`RJ+n+Hbqa&YtHldp&&YS|@&M3W*pHJ@zC2_bhOGs39DHyb8qQg)#dDCS~il zFR&|!65OC%?)s_kjQyyGqcMd`cffxO%8BDB+bT!dqwyyeF@yE{0s5b^O@2KQ(VX38 zpRaLZ7wpV%D?PCO%9hT_!9i%J5_(t+_4k3BG{-o`h0jok?<*Ku>W~$~_>v7cAc+U>k_4($3tVHq ze1Jy)HXlj)CaEFuMvv4M*Lr^l0dAvNCEk9>gzE!WD2-BhqH@Anrl-N%L){Fy>jNaQJcHPZwJ6_lzyG%8GA_5YVsxR1k(Q=n$2;*+pQj%Te+g|Kt>6j<*( zq7X5!=m#-54irCPu{w=E*v&lTKsv%kK5&32Gjr%(O@?^8_-9@YpT6%t7Wlv5!h9&v zpBEeWORS(V)H|rpgxZt8$>vPOgSX5C;}NB$uKpo?T_MgIf-6>8h5}2! zl?5RI;o~~M(;V9W*$KWRDxBa>eoYO9;b>VxPdamOb*1mCNxg(OB*5m#b&n9_?zaeUs%^$~w@CB;`3m2~@n0|QIZ|ZH2Q{fXaLQc^ zd-b{gyKJ9)d`#vf#Pu{#ZAkkmp3ZnU)x}>Pcz@LW>9!EM!8H9bjalvQGQzsQl1^)b zCC&p?8n!fqaNKJ-1Q&D}{6&a4utlapua$5+f5HF#(o8a6_Usza2dsPD)wo~}7@m>* z)$C6sucD6}D=fC^n(i(F7<&qQbxVgd(fDGl{*67N1fK5B*IR$eFxbgQo!(n)Q2c7N z8?gRP8K>Z{v2cC!X`A~t|BU4nPepO8*>hVWt5z0N1e%e>pTL3|G268y`! zGKWgK_Gkn^eeF?sz36b0RT5MXJZgMc*&oUdWWS5;gm<7Lp`uhPM4t6y{EPXMW&;~J z(3Z7W#!9DTsPWIUZc5QDTVO>a{?-1Pu!Bs7mKZ}~N*+W+Jf=SK5x&M9Mo~mRwLo9O zJDJ~UYo^2_V3BDHncw~~g1r<6u^0RMqJjQR@6#q>E82U-++P)!rSazk_@=rNg~8FN z{DEiDb1y%Cc%uo-|Cdfta)gFl8s4i1okJ1vZO)Ync-MqT zi-BniU5?Mft#Ik+Mw=}(3!pRH&LLenzb&NWQ&4ED6I5sL>|%uQm-OdX63Q5af9q9= zgJ}K8qCs1O$$9!>1}deI%`9OyZ0YB4+}Tc&?WyTO*eOkoV1*fmBYv)9ElyR5^dI3* zBL{9&V4aGdJNENNNZP{pXQa@3UziXqp}yDMH+wV6`tPLP85ZdTDF-`~IjSc{u{c_N zK{(S47e6ALyycxc!Jpv0j}wu;2ZPjaW-b!bSYLXgUSiEu7wOBrbkY%)_y4n;ef*@NJR_9=+} z^l&~YFFm?QauZiBst`}_L%hxC+Fs(CYkLvtZ>#8G{4b|u&7GCHZ`q5BA^pjs%A-uX z%M6b;@K)1%1C$7>+lA@ZKFrnWb$%8H<3;&J;E>8n(LLP1XkeDXRnf|L1;w9wls<(;A0g|>Vi|g4ne>#wlOwKS z<0PG{F`a|9$k`O!nEv6d3MuYlGu_4kX9&&GdB7X`9UZq~HbiUM`)v9Rh|{I-jCb!5 zdM5owm2ZBZ8AfW)*o#^kj61RXr~V(A@)XS59WRF)NBz+fTCMP9Zs)A33}NvD(?ti? z3QE)5Opl;m_Z7<^e6Tm;rGLtvx6aKYhmv>Zh<{9Nv)8F~Jxx3{J@wsL=P4_CfJnaG8=Q`DbXL^-#n+HZ=X|aY4SPqElOAnt?4;-9vE4L+@HZ^j z-_p<$5sH}6$ARW`KsDv@q;UChd3f0J-13+t-bW}PvE^$3>`vmc``E<_#-Wat){Bhq z$S>Cs15KfQ86-IM&1p)(#;M@vE_`xJ%JY+w}}?vi%for^auc(K|F+fjDAn+sNK zS0h@B@R;&t<{(M8_d;yI4a{1>$rZZRpSS91&}G*30KoW!$ce!t7xJQojKCb?w5{Da zDO}Ex4TDZFt*~3U=woY=C!O^PTugus76#1*MA@Z;~?Y|L~(JaiyPxPW7eGu=!`G zq1CaS$f;D=Nn-|8)O7k8d}xAMtS7)eu8N;rs+DyzXUl>hNTX&i#Uaht77|A9OaYoAIPqBkH3H} z&dtWt<IBoF&#H>DwUOy{^F@omUN2^6plb(0d6WL>}5QC`{xwIVs zxa7?N9@1OP3yhI}$t#z0k)W~wc%s+Ced?2Tg~c@4qz#(WNpH?nD)eB~cdDw_KBDsu zt;_G<`glm#iNcg$#onHY0`DO@L!mu)bRIXPrsM;+o^!`eB##{O!5QAQcTn`XxoA-Q zV0ZJSsJLf`@<0htx=12t$zjAnDKobM$ zzH37o%eXX-_a&x@Ntu=J`-9$hAxYr(2!X7)o@qBVZ5K@9X{^^cT0g33iA&+%d|K|4(>>kK-Z2h zeMJSf*hZf@TWm=>fcF> zV<4r$Ff4-r60SArv5w*JGcp&^&@lY33K|l*Jg*MCu78W_w?~gWSdx!)k!jUHH+k;= z7^$8e0p5^H&a(vOb6{R}Iy%Q%0o@q*`md!z5mN|TQ#-+Oo@3!FZQPN3GM4b;z`Wai zk&N1~Ust1HRL3Mcs2XsyTJ#wTc&RH2X5&<#O^miw0Baut_M7OqX@fWNBY~le-&E9> z<`4N=CbHNeb3g9&uVE>nlzE-6R9e@@&GLHXkr<)#M zIh#X9#-spwM6#9Fzn1lzLvvgvx)wNpvwMg)+^b>ER8$9H#7L&_93MqiP0r@})7J%$ z2({`iMiU7GZI6^hD<}t6Xr0(S?;}cF^{EnO>7djQ{K%dqkJG)A0557fljmjjPsvYP z!0>1aquM)RvWHUt67+5|sP~r2IC2i!L3o-}Xs(WWQ%WW#fni-uB=8>mEt-j$zrg*} z_?#cpdpbS!_{UWvrnNN$fz8#5Nuqm25s*^!k~He~>JMczAr6tWeKUwOM*x+*uTM!l zZMkNN{t|&>GLp!U$L@V9uC(rz=D#tO1>4%j9-rO_$|Xf{Pd^rJ_%0po75{{O{kLM~ z{QzV>`jhgc5qevRYiVKLGp!>$N>Wz}$k&BN^%qgC%b$w;lcH;)IY#F%JjWNrfJpYA zl%>~9qH%>jzn42z`aMqFSdP;24#KV1SfdROz^^~PuN~AMd@SnOIQ^HOP&nWgqa{0q zJ*Kxs@L}qLqvZ$q3kf}X>kkXVMU@^6&D5_QMIu!fIG*C>CQ0JDpQO$tVFlXO|H}@X zf~gOS6lyS^c!|j)@R*P>Tw6G0lzD!BU4F#%Qi!|wNf`ID`Z(ugAx{7ALa@G+uPx%c z1bEW=pY$ggA6-QWIX2se{Vp40MQ?u~^5+o`08aw#EuKhwZ*to=Pks7s-XmJry&1L& zgy;H7OFNs?QJUtHsQ);Zr2qFs(-$;-Df>xVM`yvveX3IUwCAJh9!?@QF>xZXYInm= zl2;m4u|NQ|Qa6#F3Gt_6tH;AbPGcVLsVlTzj+fGPOr0`80jlYNXcZs!ev{3KM3uOA z#_}P|PI;i0wkcbxgEBe8Ta}zY^vkritB(oQTHNG%07@-&W$pui`5ifNx{$eK0*3x^ zC+=KXlaw=<8+qgAvuMZZD+J6qL(XFRjiCmd=Z54+764%mux4Z@VjlxjsJd)4$`;}q z<_rYaxM2*klT?A@oPE>{c9<`r&1u;ec~buo;S3!TY1)%j`-zu3v66^R6B&5{;Ag3KP{Se7L@g zmyL~621v|hI3Me>m%Yp0^NE2nhH%6lI{r5I_clkO5t1qh=v$0iqwcXzdX{~c9Y~kG zV=h@G_kfgObjQb!W}Lb02nGvce2aKBJ;h5=jrO?L(?3zo3J1r+nUX75vvNw(JQddrpoO}}jT%xa~&146pA(o**8+If~(^`~OofQRmE)hkZOO(=jZ+}rMR?TNsuI*76ruP)in`fD(Ztc5x zFB`FRrY3q1LUGq4ScBSs|77l=ofzQkU*U-pzgOFN8^PSUa1#SS_jBD>Cm!CYhEl7< z$&b>@g+7bj0kdgqFvrqCI2(>Bh(u0X9h6}lD#aGYKJ8q_B7W+$ESh7W&}420@e=1} zI^tWgyYYtCzd2=#cyP(<5k|BuqFvnyxUbr|_y{)wr0FIDO3BLUyT9NLi>#7L#}u}sXf*su6)D-0ZE_(p$RCIQTd06 z+M5SVymbws_YfZpj7O2|c~{Sjf0X6-R!zEPj`Ga6vp9y7F7i;>UhSFuNoC*Z7SIie zdQ%AzF%6rT#kq}c9pitjL{b%}J;akGe?Bw1$J`Z&I=z(_7|`mqcHY@!wJZAb_)K^1 zYBYzMZ1+3Eurq8}HI<7AugEYdnCG2L;d}W{eSbe4G9JveHM5;T&iHwE$||3lRnq@# z>+VEZK*C0rMg+)aO88J{a3!1eFJ)Nm5J-VWa#&}=K5de);0HUbePs-XKXIA}d|x(H z`F*eb6-bdio7br9i{5jI+jY);+ji8sr+alVs6S-HEJitXe%9`}CcIE$Ipq~=8bwYF zZRSUF?7MZ1PjY@P&Ab*Vss47y@e1I$=&y84H}05txR}7c_1lwW#?bQ;gJsCN)A$wd zX8`hE)fJQt@^32xWNs@#1QJR14jniGUWUd6EdWvN8v@jd-3cRIm}9Qy>LS++Yb4`Z zG-Cf4c_(!Db}hYMp{35_`A^NoNA`j6^x^Ue&dI~16eH7#0>^Mc)~=^m-;D@r=4Ghr z#Y36%M&rXF{iXXn-yUgNwHJnIhRa7qkrB4mM$^?Ng@;mTQ-1yqcg%&F_wb}#{Ns9E z7)IR&PYrale!3AL>KQS{QKjy%HxRfoMM4{OJ5(OU1Wy<*=YGP-GVOIQ-Hx*FZ#0+jV?1{Ud%SbOhwT~xJfkK*uQsDQ^_8+bhwz#} z&8wv9{n~D8pvyxX?~C4_EVIvw#ml_S`sh-OSV4J%G3wY*r=6N~^9(oZ^=EWkaqDc9 z5`~!RVWYY`e9fz}cc$tG$R4m*O+2 zz~625aKUe$28V3f!w=mh=a}8*+ZF@7f6-C?!j*@J3z@!~J0E|~6B61ZJ4abpo_obs zmTPQvyna>ZfMYTm|1OmSeX-8fFo2>Vsl$Z}oyGbjS|Ce>@~H^$!0_5K5v zuReAG=t;fTD5Lyblq)t2uY7m08v|S#$D5W6co9xIBa(Yd{{+r$s*gR44*{J0epfR% zmQHNk0#d{4s1&_z1)mT@#qL}=zsy}aUxdKjKHjp?r+ zr;}cP*$We5^4O%pe~>Hd{WZtyi}}o@9my6QF7bLVIi!RLM z6p*2S$O6**yik+#2U!!f|G_Dkr`3Lb6$nKe`bR%x{r}CqwRALNe0{NJ}FibVKh?9+I0Fh?i|+b(mJ%4 z_vDeX?4|t`!b`+(h~jX86%qt^=;;O`1bpG3;NN?FX4Wj$qs1%1?hhdn+j?ydaVL8o zBL8wO#l_t&g^^ih!~Z<{0W7yV#OGVPaQ#cUOO}iEGgj2-F15yv{?IR6AyCp2x3ah1 zJ!Iq0xB=vB?%&n;kUvY(O|+U@#TLh4qehrv6y^QOAn zs(9VA|6*$ATl{K|meHYm?9QNT(uIJ72m@2=3kgh1?Qoq5pS#Wi^q;)@2*Ybg{U;lvR~TfN|itSRwgIiuL)-qVXcrn5S9vRr-O= zMM1OcK3=)$7)Z~^x{P`UOFg#v`X!(lmEZe>#vN)%8iTR1QM>|ZA3P0yQ^un`A*B+(6#Ig z#Agj33s8$iC9BiY#BIs68Cx!Ku*W`jTat*kDSS7=Ytpm!?__L$32x>HDEesRhP$s! zG)Vs{{(K6qdMH)x{kY8nJztc@X1US7#SD!MX~T8wr@aSeT+FmB>a!**4;b&&tr$Nh zac zyyC`3@rN+;DebUCNNv??oxFLA?@*QcV;&U;Szb)f`$o_ehNkxehcc4X0IYEsQ;u6m zHIKt~`3U$z@1yacy8!sBq4XdcJ##LD!`<8#S~X}ld;d?6@qvvEU#+-Cz*Yd&m+lCf z=yYp`#uCEkaV=zybsHUw=kQAM7qW2Srzfft>HeDX z2Ueq&mvwwQVg9%^>v7fRccH%>v#bt)^sVh&CvZltwp`J8Z4n6dZ2* zVHe4-eJPX!JXNrQPoU;qei@rkaonasdVWcDOB{K&(r??YTHI&Mba>t!z`^ zx$s9Wg#>(FtrodhDFYp`CmVFX@jjGlPwymL&DTCBWyniT>2cm!W^ZkaKwFeR&1#x8S6Bz`Us!o`!Y0-&lUzGdY_W+J*w>d!Bo}~X?5RNQcVS( z{ONwJ!O#Ey%W5KP3E*sxNrl=#MVJkb&>D?tP2RYhwbc}fRGe94=My0}@PQ9Po~qHn zxIKe5Zdzz%)BN3zM|4>ZtT9;+68=+G9`>s@fLbc3GD~)v@x}vJHU}BsxoSZMF4`R@ ztO$6ISFN4AFxC=)Du<#}nHfwZ)u28SY)JKE7(MyLWx<3%rLD>{fK@#uw)eXzL+KDA zRN3P2U27fUk>dlHJ-dQ-Q)rJ8c}nx}OCMBv?ipdglezYnJyUcMG;Q|$HKV}d81r`;^KVkfE0zkv|M zMA`i521%%YI-hU&N_n1&^18B7^Tdew_4?bSyLC{i#jMAzm&7sO8e@}soXU|^>Nn+- z>FhMngqvCRD)`i**wbKxLPBf*qGgQTLHISyPUqX>w1r=Pn~6^V7G#0)694)BteQtg zec>JJxc@|H0DY4#{=cpPWWUULk@aFW?i8az-f>2Q=qM{bkWs6)u;6TNq@ucY3Z}&$ zCURDXZ-e;?vh^n>&v;dEq5E!}N3Ua->LT3VN@mCeetr&+fnrLfzR1Q`7crU*q!BPe z3yqihssGqrC*Wlf$G0yu#yboLyk~}SJ{tTv(@L$l^Y9e_6qm5vyQ;rXXC){RVtP~u z_k4Ja(P7r!kqf=97jlFT4k76IoiVAJ?4Bhf{gz$op)1zY z;Qis*U=9I6BU}j_Lr)d#(7D6Pt6)Pb`iX8sor}mwTSc>*ZN_%>QSBSfuSY%Xr0;4~ zMHw^)78?3c5~u&V$X4{jmm$VT6OEqw)elWfk%?A*a-<%yXG(q9BXic3UAvC6+i?0H zcjKd2OWGMNQNucq3A0)fz?lRDj@MTUzd#OE+BZ>JXq7N|VAaE%R=81beIEM3me+)# zQSC$gcTphPJ*6T{3&y8qD`i@FfI1_A(s4VjX8p2uyJ2EFi!qqHS?MswlfkZAyE0`2 zgsxONRQr#}#MS$*bK(b(t!vy8w#`aD4yKxN@=0GpQv8yC!Y6A+BdMq-w|PZnojbO# z-4sf1TOA~wsS_m|B>8`kI8*XF$Bxg!EhpU(%o$Wu4(>Ey8?Y7Yg&Sj7jG zUkUzy-!hf#FjABJ&KNzHKkxhbK{9$S*af99D%bvyvHaZt$D#lVvpfuJDY4-gHYG43 zD-#lzM6%!&;H8sQMAFR1b=u0!?VUSRyQkV>)wneV4OaYPh21Bugnv_kAkob6FE+P` z#gX?0;AMh)(&usG5UXShtp#*6zx*TcazzD|HcW~vf|Z}=%o`1Q1ShOHsu*s3Z%Kh# z|7-E*e=T5*V9&yv|ETZ}i!fSb6$HD53`ojev>Pt=Gm-wU$-l2fq0XTzi#Dx1ydTRK{CWsRVL1kc)d@ZVL9J*;}~{ zxbkhAf5QX5EHO@xdCRi>4~f!jC9ZGTN~-Lcj^k79*6Q_BMJ!qoQ6?Bedqd#thL^&z zVt0kx>Kw8r^;UPOJ|q2_=tt@2k2nmybKqU-!FSlK+j|?C?}tIHClLE~b=S+N?I1GT zop5__fjpG?`JuZOC-2&F2pr3hO=&4$r1O951;Sj0r>GBAQ{oWbnC154nqn@(*;^wp zO8$r+vAPaxq_RT~|75bX`(otqiBpQ5|FF!c(IKm;U}1}qNp|b=!yvyc`N>~F4jF6O zG5Oi|s-{7uM9t?Z?m|)jf$AS@rH~GC`msHic~gs;qJR#OxRZ8;^mKpkARTY4B#m&{ z*&EQ+|2!1Ujgy39d&ZDHqM0aqV%56sU{hx+lXS?%#n+&wxsHj4GW=ltg#9rp21Sb> zyG)!c!b>GjQJo*bZ}<(XaSQD~2*V_b9EL@dFdVjG1*L;kPFbyGD9<>)a<>|9J3{s; zIKSfKp9CY_FVNw*IR6%X@=Ze#Ro^k`%}cnKP4Suryb2TccrzKxurjCAWXAFEz~`1_ zygdKL5R@Lqi2vaRA`6=h*P^K#tJFacF}TvP#^&#Qbmdk$0fd%o$U?CsxI)&qwW&9%xJs{x^QK$A zsvov6v3aaLl^(TbwfZYF869wtm;FD4BxC2a+pkFd0_#eXu#q&;5=0~68qo1`+wJSu z)QejV9A~o81Xgn12N02?%VEC(E>&k{{lra%gUX}+t?h&eI|y{dv5b>(AGs!d02BxQ zsNv^Mau->@lp@Ax8J}VJw?cpu)g(LmOnQEC&IY*PnSY+)8amSIi-l!76tK$@uBil3 zW}oeU_!ybEP>3&sbU;=R{J*^hwptPd_tr<*PwDJg$)Ky%6o)ke9AmU(mNzS?zP!Tz zUZC_E2uDiZVQB{NHCMWmYT(H<{Cg`>qPnZwnuBGEEYhlCzbhIH4;#VDazZd(`UC-+ zH!GR)6s`bE8JI$U>*_$;H`=pXX)sq^m|Etqf!UqwOwUrCPzbM z{j(2ltJT^1H4Yu&K9SOzdd$%oCkpYt4Bp&?3QF7tHC9;`#nfAFd*f7=ilv=y*v#q9 zBHp!}j%m?CxV$zNrTtvYtDx##CAwYT)?CTr7Sm0-KATebTI4OaPm}XiB<`V_a?js4 z?!$+}a2Hj)OBXU7qOPp95;4S9V*NzfC+q9??+agxD`8 zVO~M2PlsY}=5iT6y?n3HQklFzFr+dPJ~6OD#miMQjMbmlVs5*6D}z2YVZF0UjO2WR zPuq8?_z4}4cFjE}o`C-|--sF6*ICMey5~p-LVb-&Uy^xn<#v{Nz~YX_^<3cAn<%G@ zw|9w|?`cADsZ^E4?gP<~g{nTR)hBUWv1Z=LeQ&kpniLhj)Sw>^W#{@O z0Y=PqAEMj0lN!FP;`kqkgv7-wo1?Z%r4JQ3<{Jf2?lAXbsvV5DkBLe-l+_P#d*5IC z!#`dND3`id^_UE3#p|m0XZVui|K!Huk<9}fXh^q}D|K$TuR5Yg1avz;B$SQfABk4$ z>U3NvW@{^kfc!s3bmnc1q98N;#8+Y$PfyI(m>y`TMQ%koXXjcU^Zj+&ecuM882F*E z9qpO6mMA%eCwNV@U@Bt!{V9XX+)#jKjV}&q>(6X4%}X0C68YjYsN(=>Xp5;lXS+pq zAYa^e4D}MdE}#p=BwBqq9&NGxGJWbJYHh9sw1O?MLR^^24IKEZ3L;) z!v}pr;(r`u4pq@#bh}mAx&H9^KA5R!ZdF}qKxpZG;yHos=S6p^r@{+%o&Bhx3cBU= zMdZa(Gd%H<1fpQJ`~i2fn)z|~MSY1kx>czm0p`k|hokg=Sfo0a4P4DpA?6r-7B8kv z=qsTWhkO_6LW^2H(vQw;Oxpg%s>*6hR#B6&{U!F2x``zVCp)|Pu_>tH$hx+k$)wizw@#F_ke2rGf>~P_86*KorvZ~ktKPxoP zx0WeYk%m2aJ)qh%ae=`I#i_plc&rbB& z?On_y&D?ng0pS;~!l(6s*XRFm%zgCe#OyD>^;2KY`g%8fEgk<|G!Bb0w_V7RV~q*v zfWse_8`3bw0xLAN+p0{EJ~Ij$jWAP(P3%AY=b3~Q&)L*uS7^>(@IynHV%D&>ru{E- zH3|$b9ErhY`t7AWMzeM?|Iyqru@W-8@==gAN}axnw#U}!jY>$q&5)Lp;lrPX7UhYe z!OU~FlXv4rqvJfb%}&`OwEmj~erAToD!=VI5X}b0`5|G>R*?lwdqj&{h9!l2S+`sT zKi)z}Zim+>^-BS9%ZjF8?jwkBaX$Sg>Aq_jJBjDkor6bGumZ~sW-`D0tH>ouZC7!A zMS_1%qI~f!wMw64lwJMv16{2KNt;v0g9pxy_Vwa`$j4vzK8dKvzJgXb*@ygh)C@cW zv96+UCu-5V9}@2D!xwBtEbiIx088I?rw!S^v4`JpxKA&{; z=M=a}q5;VfoL5@x9j1@YOw_8y60xcb#ho?-t09v%00qFRl*q5UuXY?lm3lTp8hAh} ztM;UbxA{yJ5+++Ce6Jdmp<<2%$$=^#U6pENs?(^x{eCo8(EoL&lsJsf-)R3%*9+*X zW?)%Ll$6Rp8UjX>icIbE;KRYcF=0tH3yO9KI{mryt19ZwM~^`7QEIiyUCYPd+~HQe z39)Rc3xWBSOgRi1mqh-iAqL+8H&a)NvI)FRzia+mJK>2o6BErZOpS;iQ{zFR&gA_G z9az`;*6i{AcJv#EW_pb}T-w-GSbbQfVWzXJTTf&ro=)OlQcnLmYYuBj5ZICaFz<$%0wVwYGm~WL{Y!#DKrg8LeF*JP1vF8PGS#@Fu-R>)zB=?CA>Ym` zeur+bF6#n_%OM-71qy>5hKW&FDr#;NDn=C{3@j-6A$ZFn#9r`+gQ4KBGY<<67=Lf+ zvLna)y9aCD2YMz{UuGQ1C;YT@n4h)5_%?3O!7@Ix5NFbNuymBB5>zuZcdXj)$qFL! z(51sCWUSm?ZR&l?<{~m2KUf3rZZTun8locto?G9UU|w_k2p;J;onG=f-+nWSlPy;K zy+0n8hC&!;N*ayh5#5uctIn3Z(aCxyF8dSGeeE^s!5GmZuG1L}lZ@#eu)Xpcw>uJ1 z?v=exEdxx4xD(M#Ck1-!i)OTwyvvX{*yZv%#f)>0lG7}U+ZwM zdYTH>{453lF$L!P-v9urQcCvi8j06z1}&qf$|gLby&O3?$V$;x;GFRoYj?$sw!%M+ zlIjca>)b=A%to8s44ed?nH}jOTRRv@V+Ct7Z4gsG0t`V3iiI1{v=Seay_BA$cyrb4 zU9RQXVQ7DKcf6gHuB}$N0NAm&FO~|U9%--1>Lb z+9eD9sKI0l=PW>5df<4esq90`*EI<;APPLij-&YN2M7U;SSQgf*zjQS=!f-R;ODhO zqp}QSom39tj5e+zP-CDEjZFPe~elH-8vX{dKKOk{25_~ReGd*Pr!$s z05A`vuH6?bYFQKgl0ZnuQ}h!oa{Y60SuJPb`!SP;1$6Jwj-t21%Kdrb7l8z;sW_57-}7lg)dBz=Dsyk*o&;BNc{+i(;GiV0kRg=a3j zjR!WtB|qHO|N3K~YP@__N4ZAa2Ja(D#Uj9)CKexc3;^oAfh*sW_2&?&g|<76ldYjy zSN`hv@NyWcXf0L6}r+PBF@bGTw!ihL`cW^J9ZA zUhJVFG10EoA+=r5v2E+E>p^L{@S;787&@G_?kJ5AoXZdl#4w9GU>Am7#?K!-}#MjCsv zT&6lChZ`;zi!idqaoSAzhRU}Snr}qF7A;|zeR| zER-m9R;NuJl2 zlb(u?y!_Rb@c7NRBSYcYoWObpnb_=2PiJ{!JB1lZb4#k**`_290`ME#$KyX^4*3!H z65D$xKe)MW6nNl|9=%3#J+rbMy8&7I8g6OPGOjOi24WA_*xcYrLp`nhW}&&ng-?t=LynHm@_sMAmgaL@SY|DBKFQ7Y84% z6)Tsn8O#Rnyn~J-1U_01p{|P}p4hX97X~l+?9BxHd5d2te<&P*5AOoH|02VPWy4pg zc4_9Q+bHcgTY~hKaHjUiMK~r&`F5DejC&_GCh0)7o@UYmdwg?Ne&mgfv;%hksEy>& z0h4~i!Nh5SugE*QNg%q7CK&7DrR~xkq_+8IOlVBkE^qA|8*W=UD+p|C5}}O=GAy~< z`l_QWitad;Uc?OI9twtcD{=GgYa$;gBl)8Y~_R4i)ecLz^(!|V~ zjgv;>HmGknAd(1Dj(cy+Sf1;X)qXUmGGI0L4A=<~W$tar7cQ>qQ~=Q%hHejjic zhD_-r4{D94l8*@U*NOlRUDM^#=Zp{WROBqBD;#yBc$UZB6;LK0`tlu4=U^mE%XV|u+|ZgP-aSt{1OPM$J3Fv z4U^lMavt&Q{;qs~b6bNIj|$y@#SgC`aM{;rMq0Xve^NMHyMJx6fP>p3QurjPwW8b9 za_ao>qD=TXT{?zN#-}biz{-Vgm$bxnvHuaW6#;E55+C2`9(`uYqpv zC+LF&Wg>GmAs9u#+!l%7dz%X*0|9McGe8?>kOt+C=ww(+NYDstHD-P4g{vAk3#9&v z4{%J9qBSLEhSQ7%IrmJ)E>SMy>~1-?y5mfj3KpvwZjp#+aQQOe6AZ@8_qp>BH3=iX zS(*KZ!+^oghxt10j~+(}q(gl%pf=ldJ+XUXMgbRuYYy3DcfIs&PRS; zUO`l|f1GqEJ=HK+jfwi*)erjWJR5cqO&9%nh||%Pu6XR9AZ%l;ezTpP|GfRyNZUQx zG4r+B>=B$Pnll|6To-LT<*YN@AwugDI=ysW%oV^yuuSSf!13v=vD|~&lgo`D>ITWp z-aVY@=4!k#cEKwB-Q)o56xl#i$asjd?+E%LOK(X)=uCx)CZ=gc-oI|Xe0-RfhWtdo z#&W5;DaOA!w#sHcH-oA-bUCMkQA0;g6OsDlBa?S(?T1FSpWj?`z;J_kY5Gn@DoCTU zZR|oFzY#*_>_l@i!gsBN%Y^IE+~-VLro(>}QLt;OY7gn`%BO=2m^}djspR(hFM#}n z)XY9m%7vKBka+%mVGiYwBe~jYy4+CE;P0E~KKBc_$LF+0Iq+mhd8h78o_<4Cc#XUt zS?5=U6KNcb$tFne+|7sc0oM*AASHBN%Fc#L^jJ$4D=$-L&bij|jT6F@^lx|7#6?p1 zL&?1q9oue5#{F@}#{zSlfA@*<3ZK-6tqqpyl8jAI98t4dPBsF1$wWQ0Zm{)bnPkUW z>{oumPM$WOMXyuX1L8$4A^`vZ*F1@XkLck%H<3lB5n;bzV?1-A-i>BQN&7`K%TFO_ z&WE%K#|@X572BV2ms33HUpz7+?LovQ#xv3p7@1$NX7bF52KXmc9!&CoU;X01`%V^{ zQ4iojj>w|Vkvp(=^iHl0xUYJ`3e**)fb(9Zcnr24uz##qF5%X>$8I%rUxC0uSw9ty z?NEchNwyy&pkB?!36Ny>zpYr3dhfz>tSaB0NiSxkCloFqanbja@P<1thh=Fa;W>zx zl3zT-U>l+sQr;O(09_-$c(*x7pKwv@Bl1sEmJBUt!{%(4Du$I zq8=?9>~!ELD~AtjAlwAFHa>u=+%9)DRmUE*4zRr7QYSTL8JIr4M!TeuHm#FLfm#M^8H3eH)S6v0OWV;wJY5}xJOkil9$lm z&z0){xJGo}a-;%oM78(R{&^))dK@Aa(z!0yewix=>zJtuiQzQ7_bN2MSEBL9UwddP z$sb!Z^l$cFo-neQ61_FNOoDvo2@AFt{&tvJ23`<UnkplJ6yTgR3vGOqQ+^MJeny zdfZGV>hmkdbKTov0z-Zm%0zbsMWPG4tv@4c3U%QOIbLZQ6CUtBP%>sV&Sv~{+6~S7 z>6V?N_i&knd~+;E!p}nij{5ka>ezP(e#eGKO&gn;ne1%D*x6?VM_ycIpM1lxa^=g) zBx_y(4#jkQ0NaGPe9iaHbznPJN{p~m)aSvW8x076C{=fX@NY}){tYs)&br_3}Q><+C!oKJj-(Ko~g zrV@`y{jNIQqZ+;zc$Y2U=rH-5*<|Cgc3PfoQpjnpxdZ6+1}rnAns9;)+ot?Cdaf5T z_2eM+1I;ydK6XQmsP2pUnG&vxgYTMWB*uFdu@_L%{9JeTB+%GX4#Pswb|TkDxz-})Gd^7 zy>(gr$x7shDvFe26OsA!>(1H>+@EH>A7cf^Igm|Fjc4hmYLb){sxPvsnwin*fheGEN9XBI>{FKd{ZZz$YdT&Hm77P( z{Na*E6+h0lJq4}Ye~0>$F}VzlsH^K>Ta1~2^Du0hJeE{-9s<>1_8oqV4*>bpp73wD#!ZLMPHg^05sK3P z`;-j84`vc)t4J0sXC%BKECr^}r0_tr4vVje?KkbNDU{gE@{KLl7%b*@!ahwZZD`)G zh1G{q%}Zka-&lrMiG6N1|J|5i;d)EG=|fUc_?rcEUqp2rVsZKF;&*4&bx zn8#&hU|qicDUgM|F)j-;6O(2fue4?xvO`-~t0h8Brb5-l8g|tLVx7#p+pgx6<`Mqi zxDSc&OBB&ir#Ov!QO&K1#u413?IYDAf5C0L8MWQ~ww)w4XzgJ8e*<3lz$~B2UmIoA zsLtcuXich5XJDXSF%^`XlBOO#>(k|MLn*j<^Z%^=;B~!Qe|c4*Jv`T8V#|!=&myxq zkwXGpq8>*XLNIM-`QJ79?O#%vS7?4fx6naJvT~(fivOPCkjU%PrG@yD*yB+nm}64# zWl7`;;ZErKm!^S=%mK8LA>VF@XY$bKQNO*G^5i%0-?c{TqRY*)Wp-6A z1$2g(JPn;(w#=p5!U=DLuKP~ns)|sr4& zm&ULNB%xAwe?0&9r^KrY1Q){m@br_`)w!wypVD@>hNkH?4_MUsBMaO+2`Sarj6fzoz=NS!dy_-*j-z< zBV}Sw=fU}W>6q~7q91MlPYg#E5j2X_9ojF3Rp)!p*Ou03u*k5rm8V8@Nm2UlOhUz? zo*bIXaL@-+$&HXxbgKeE0$ zsI6{`x1~_rDems>4#nMyySux)7q<|kcyV`%yBAvAAyC{kKwtR2d+&QQZ-zg}3`|aP z_StK%pRG}VxCVR4nwlM5!|G()U@{#u69A;vCsARyALeqEz>d!*A#zkeHpe#!N}hJ? zX(*M@Yq5W<(=*Dn*EQr;7I|rEx4L(9+YGn|M4=F+M1+QkM`x&a`0OQ1=b$1E;Yuf$ zJoxO4(M(mhu2Tp8@!ISThi?KvHQGWC8Uk$HiMys|pa+eZlfFT}2)po#BU zc1!BQy@erVc{&QT<~Eal&BoZF*neE~C7BgPwo9a=;2rq&zK6xd|M_xc>JY75P?Hvm zzrRRwxr`(;j=4+aE@)S8v+q`re$gGSM_KUEy%V(@vk1q~S$L-bLb-i{l-JeCkbryg zDIbzMU|GJ;$7$1R${I3RtisuWpeYCSKl9^qwC1Rj-}#uX5o^0!$I6##i~_#tNb`5Tyx@)`(nKpr zP_5X1+c@j^PG>jX@%OpkGFRU^S$-E7{Zv{ZyR?>3n4;OWN=iTfdM}=H(s9h(`rDE) zs0r6~P**%T-JX8p_64mWeBrl^o-E#sxw_8qy%?NHgT=MwBN0mX(;khOUl>#-q40A& z-&L1G4I$aRbHNvO2B!H7VYOLq(iK#)TyC)o=7Kqb_FX14Mh12&!ZHG8d(Pa?R6cx< z;zSF%MWZj%2GVW3%ETiXTQvQF9m_d7cut*R`aJSy`V-&9D5LyLbG#EmR~^k>mOgI9 zHn@m>LYzU+c&DNndS^zJ8vF^yC620+kbb(BmNsL$e1|MYYNZW%r8Iam2UAw4(Nv@9856XC z+7}b0pI~U9g$6pVqnj7n&Dy9g?Go!Z2zE-;2c3vU7~E4syrg>+I|d%{;o_z$y55y2 zcegCTQp&>3#D@t#r+tNypdCk)m%;>w5NPPmx`77EII1?;Xdr#vF7GG zHf=0Bj1+dU{d6`~gbw1m0wQ9fCw<8Vf3m5Wu}>_c-YI)?NBaVgGawE`8|Y$}(=@XbQH{iSCf zm{{8y<8`b?VHH$_dWj9lPm0y#PmfnRESfIi?--1|9c?L`RLmW{T5JVjds; zO1D%aSCY(2;d}72c;($-Vg=jPbejz;d)GeHQ2K&up%j zj}QEPB-mL}vUP@qDB#=39jq4d$}1qasG?!k0tM?N5oS7J06cw z`@0mS2@bJ#OPZ;o+L#7T+4})fdHMuW)2)zm=@~}EVT5DpdnSLvoDrS^(D!meI<9u} zFm>yw%04A!D)sRul4+II+E{ecl-7;KN623&sUM8cNfhFIxvmUgXHaPQL4v$n|k)NZ0Q_pA<1Q_Ee+OD0`@ithROsT8c zF8GUYQwkTV`P)us66Mhu7+{=^#ddZWz?dfVfjWJeV(}*Mi zEPJ10_55E!-6qC%B%v6jJshy;@hU${zHrxQDKg#em<_c@BLpM_xT2L=s&8aak2m(` zU-0+ywzv;}6YL6o$*fXbvI{ZnL;uta z4sP14KZW%QgoFDWt)_1^hw3B^4t)B6R2Gn^cfTTKMJ_cZU{=!iy*DT=DrvkC49%Sk zi?8jyf_A9A^)4N6QCMx`y&%iF^QCO=+Ce7e?gQB?{t5fI-$##jC}`$+u`{2oxp#|) z`jqgrjEJN6Da*LhZ3nmaY8hsw%`{2=$gtWc9RjRY#n;24-2oF!28FCYW~VrfuQg*; zm2&&tN~IM~e^dU}qlh7ywZt91JphhQGj(eevTM`h3(BbQCUK-7ay%p-paMJ3JEr`> zJwd-ADdp$cu&8TJhThxJYZM8TC$O?dFcUJ16i@;dBoiqobv-($u}zyM>pn}hjXy3r z$!E4dQ|VY8N22!L$d3wRUCv&D>%-dnMI@GR5*ADx5=!iGlNPHz(!^52Xh21$Ra_-@$B8^5sV}ipZuh%t$StTM}8U@i2n$CaEfGB;) zJ?he3hkDE568tMXC($24*#o1_)L$vz4<`RE@_=LRbi(NJK2Tm0eNNi=bm(>biy7Lv zL!8-VUrFC->h|YtAZ`h%#h3V9-AYua-|Zo;nSQGqi7+!&V|YU6@u?pvFt-fH101t; z@k~fjosp8R5c*9dKhAhE@MFg3cqRSyR$`t;uKl6v27XqPe6{r<7(s=Mb{jyh6~iVx zPaUKa$KE#%4wUWQ}td1(({9ssB2_}36w^8C2!4Nx@~NUbe#u<2Tu zqdI>dkSU=OF4=Wo=j!QFtXLv5r|&gEj^&9P&SBabd@%7NQHK*~L5(}f(^ZfSop4}+ z;s+r~5mYbhyb;MMnCudxuSHPWIe1Fyp=$d>(eC?l+Bl?Y)RfI-LY@>VaZs9(Yh!HR zA1YPV1&XB1ls-Q_eMo>gfB0IR}Nv$z%o-RMi5azFLW& z!y(h`i?4q|P>$M^^2HX&8rvuqlZyjDi~tC$(&tf}AsA{B@}G>&*vb z$hxMA2XXkc65egZK-;NN*Np`EDW3x)+YIhp+wtGMRT@z0H859XTkbR)jZ)7JA#>@B zIgIxHRoD_$R8QtR@1*-J{!WxchoLM-P+WSlh6Hfj{?!`7#BRi7pS1Na9HFPT*+N#VYJ^RUGYAMN7&}7?k|Wxd~e4=u1StXne9@ zbN1;`rea^G)EE!vq`3vJ&0Q+x1(UK5fvE7HhwsenvUQvPb`{$%ta81Z!~io~ed9e0 zzZTV%v}KYY9W^29Q9CPB-FRe9zcz>C_L&KF?=b6;kj@ipC{w(GF!>?5hWIz#zboGu$M?P`q5>`OO>RKby{^We0Mcrln5{{N)rDINwPzm_>eGq| z3-p+#2Gy<0NL9hJjURfYTC(D*(jkACys*NsOg2Pd<;IWB`4dEl{XmdZ73eCQys^{s zO<%iAQqbjbZO=wptf!|O@j>sqweG*KU<-GX{_b{J-YeEx;0K`+@PZnrkq0QfA@43T z`~!OZIgQDRCWD2~ERl%kuao6~vJ)$^t)kN%`5MmEme%zvVOAzzLy~8gZ^8a+oFn=u z0%pf}`>_n3+4@+3FjPTf;^CK_c0LPvUkyEBO*&SGW0GjTSY@~#a^fawOs8~4FQ<-6 zDZYiokz`9Riqi_$fOVHob$Oq&sB-kA=7w54aDWK?AS^Zv#=4@j{5#jYy4=;j>pz@hM&COQq8 zMHonT{CQx%jT5n92Jh;xXlPgbg6&R4`7dzXU~&wd1R@$#{mt%vZW1=a`;Bz(O|8aQ z*DM}+_AARJ2a%y>ag zu?>^`J(>21@5&nr90?@K!M@zmGY$)ra_w3iS_ut^JTB{LTDG|^cZ(~*LHf!QBIjas znE@La`4xPIr`UC}iz~zAeAGojT$|6hw#a${zL9wkP#D0@+ zLM;Q+wAsze-4V&)hC{}eI*#iPX!hW{b0?ihI;GSczq57>n%FBGHT7RA-+k@uR*W#F zo8`o29cx+^@e9x5WA#z#sKXGuQw>%oH_`Kn$J z_bFsRZ0WCj4-q=rh&C-d#SjiVsy#1X7{iQ066BOyW1-#8N~3sY4RfkPr;PRuYQjq| zUXG=r*73XvPBkhHd;dvyQZom7HQC8Tk^qZ1%e{(>w*~1$e<9cd30giBE(_?lN@kXXW~z zNg~Gu%!>QWxA5Yh2ljUr!uTbe*AK`7my-I}bLM5QP~iAXS$}}V8X}DeMW8lZ*dNep zs;6H80G9?()P24q|ITU6m*++sQ3m9v9GrWe#Lzl5WB(V<#@GhsmWhO=wrV-_|`qO0jK$VH!KpZds*c!+EBBiMbB zHw_sv--uSEK|?hbpO)qMLVd)y*%2H+O$bf)GF8_Q9>rW{trH5j$I0~mu2JVi=4=5f zd`h?ih*l=Zy@W~&HYuZxgV)nZ$|z}ya0>n9SwxHYf%vA%ZTIxodJhX?n@uyccJ+>^ zygYKcT~%be`0)q#ILm=sm?;1h{fzUO@!yt|rb+q+oto9v_skJt1Y?A2uBMuGWnL!s z6p|fzL`!=kf2!Q=h2kEP6&BukMidaO#S|4JnCk#Pecpfj%#7BD69!l95T zeqGEOFK8~4jrzqw_oPNtOG1&xkA-U8ezT$D?K0>ruW>31SE}?s1b2%)U}etx8|X~x zr`p%g_#go8cn8doToq$DDpB`hL-=~#8TH2l3s6Yne0P8>#@U1Z4#XW9Vv?Vlsw;yh z6YNGgL1ps=-}=$Ky7G&XP9$c8rr{2yPJ2K9Gvm_;ks)8roWm3kWXoOtHivhK{3~*5 z9aNF*vZ|9=G--JYL{x=m+F`_U1>oB z?Q|3ingOpx?-l?BbZKi6uLcW6@V&CWcLxf^6tIzUD|xv2f;FF%IgQT|Yloz4R5}`U zsKHxM2*}IQOT`Tt2v!=yqXK4SeYmVurawpho1+ygQQ2Q_j~97mHwrc}suP&Iu$#Co zZv|jJgW+9EM=jqoop{U!n|vieSzElrV%XJ|aj}g265f;WK>ovdBN~dsfsBZwT1ZS{ zqOzwYvb^tf@IDCnPmK%hIRAS|{i-{od&TDj>eLmZuQG)k6i8fd?I0xa9gGd^6DQ&)f%=fPK33py8NQyzuSBAabd6=&xRI*ld?C!OCld0kdlcUFbH647%H(&F zk9D_Qt)bF=)V*o{!^#&P(xOH+n#&LmnJ%t}ysaq+c1sWrSu>b=GU@duTw~)8k5-WF zmHP&d&BHN9pdk)qI5g-D5{ z1#Rnz7X+huvL%H`-tfsngEv~WKHBvR8|4pWfQ0IXUBGIW6U1=#FNZKmxN4;Mg!r+A z#4J4kYyD?CO+0DaCBw_$DYN8qw6jW&XOG5Rk))4R(Bf5bmK4-p0woDj|gI z{09teZp#U{Ti&l=s7<^557i=A80!8~^gr*<=>S9^U~1^I?m?Y70dOrbg@^=CM0{_) zx9?61SOg>+<3~Ogy0`7u%}dN-1sS@%RG!EY0!_=!<%S9&ekpO`iX6{uo&FyJmxGw= zhF}*E0Ae`E6Y^Wj>$}i@(+*VKKk;67EOxh07Px>%vr)zX_CHUJ%7@Lo0n-i`ROq*=VLXXAk?ZcD~ZE``= z;)cRz$C~IyBMC&9A;U(F`8{S)J_E-_PTUSFd;0g0j?+C`P_10JfukncI`Y`p7|Q?h zKo|$xVQsZem+@u<<{C&OSPuazY zg=+EQ>f%_(iuTjmfGgc)ci`HatX#m!ex++!@O8;-AKbcOiz58+%?Dz%cnsK%z?oc_ zSI+eBZtAip4g&_BbUnMejgQg91@DgFE4fd(mGIrKxq)&7H{W;x$GNvlobj<(D(sJ0 z766_Km~+l1z=p9~Sbx#Sf&lE0?<;M&09#Fx9$D=@%#_snV9qxn;sR3)0__ow&h4+? zr6I8{)#YFNA|n(HPzeS9w5$k1?#g~|$cKhN-NJl#Z2OL`0I=uvpQ6N;>{?}@PfXdr z%pE(?ej(~%x|Y5P#4|!zuXH-pe>x9Q6b8nJD?Z(wu3Di8J=fXO6r|7V-N+D$#|8Yn zJKuE5gD7v8Gxtt;HvTO5ex5L{75gfMoQUKWfV%ZWtYokWp}kl#7P?c>M23?WC|pk- zTn5ki8qc@F_8;nZ8)oJJLj7%)H62$7Zu>2bs@p+JYd!}xfLQV2`{(N!9th;8cHCio z>-+k6;8#iqv@?7sLKr>Hb6!ls&9wn5qL9hxuZ<chu-p$KR$qC@bh}bq&B;F~(BW9e@KWpFLZ`txbf$E;ggy{Ds_u0c^Tb>r% zdd_HR(q6M_o6F`W$KGsnZ9V6yn}MdS+j&%+rvu>)SQfR^S0w?^FHP+wPL?*m5wOpl zj@5)O&mF!Y*LJ|cmOwm-%JLU>$_sq5IT+AXShi0rWIxQy z(9nFgfhnTBo_Z%V)@`;rEy&Rrw7jbU?rOAUxp1eZad?4Y2+HCkhePeKv%6J&3XEJ>Gcn;v~GO1x?rwXKRxmCqpKHX0DBQ$B$B-T8yi2@=4h~hkYajGv>K3l) z5bp7GSO%N|^2q<~*KMYL%}TJ!_sV8D*XatCc%DsfgLqJv;r@(aHS*h6{IT(j6;gc!G?HqJ z{;AQEk?pn(^2c8~=JJPhR$Jp_jt@3?D$O?sWdWMr1BOP~sxS2oQ?aiMhJnpKQ*m%L7p!{L{%3E*_%Ur_g8g-4iB z8E=TxF)i?1oCjeHsnfNqQFdRptO>T$Bwsf#Gwzx`fP>B+UfTXP$34~oVch-ZMab@A z^oe)C%t%FfIv)wv2E7c%YYsg9-jBC>_Dw;-aV|2BiXM-Nvw4$F!sYL_#5qYLsPulu z8)d=)Qc3PfmxD4Ehkv>O48s-E{n%J8HU1TaP@9<}9&%|JF-;v<`}}F1tw{P^<)Pn2 zb+LSqXUEm7ue;<&l)l~$FN}#lw)hYtLIo+TrxdtDl-OA= zb9l5PBgOu0K^IwU$snR005XN1%<@|Dr~W|1^~unR#xx|v2vN=FHp-#JrYz1b)!G@3 zDiO`KTB)Jj%VO)@?}bLMM^n`xh9I!T0lQ)$Mz-!LmDI>b8X+GyKUTo!L=ndXG3zvg ztd~*HX>?!FP9?4aW@2^RNXu((u%ZveNzwPaBBmNoVIXzW$yxioq0Rwp!rnUt!lkH1 zDX~A*5%a-C*nS_8ANBy5-v3dl6tQW|ci#ZBL!SDKqP=GJ{Q{j+UAC-&Sf9d5!y(rQ z*$Ltxi}-NcBGUje*kV~CEnJm2X5<{Bez_@)N93-kHuGikxdW#x=7g-WzWq-M!&J*@ z+lDj>Kp=35Lcfg(BGnvGbYpZuNU+sIGIgu!>n`#nss1Xz)EXLq4A%`}Wp(mD)&t>! z-Gwh7FsX4|!#9@Fwj&p&6WluMc)v~Y33x`=HL3eAx&3tAw<J1U@#Kvw4>|b9C2I^L}ze1FTdV8%qJM!6Ivty3GVDcOhz0f-}b8-L9wqa2T1=-Fv z#44@x(+|#3W)|&XBs|Nr={oN;lZ+&E)AnG|0Jc(SRYvP^s`Yl}!}=p&$P+Jkzp4I+ zX(QFocMPvL82cJ04>x|Rv!RG7KxZz6xb1xHfh4Y!dy8Sa$- zE5o|6B7-@cpAn?!!6IpLoy`7?;-t@;ZHN85BVRdkcY1oLs3drZ^~m&1DQ{+o8fWNg zg1V|3&(1_@O0^O#bfyF1ehJqul1vXnLf&~CI1RDk9aYRG5}3AwPb~H08(3-C$JE)-Y@n% z{Z>a$swCQId-y4BLnVjEjiTw%4^NXVH4wW10x6oR7Hd0j#_N=mUXQjU`w+uNWY;!% zy+8O8-C^!#pcZx^gwS^gJWh#RU33QXCe6Np@8vnKvXX3Wdw|Rtxh~R z1|H3~Tm8Nt`$MLz#uA7G&|0ha+w5v4ybaxdO1j2};StG3#~sbgbM={c| zKHVY{w4jz-tzBqVqSBebU+8uwfv=wYCsOhNW;Rblf(K8tMajm+Kq+evxB13=Y*Ird7F`{}H3@o}c)`Dgqs zB8|yEB$$f?rR)kERFZ%!Q3=|n?;8Ae$c*2eZvBp(m0nn9d~Vyfubt*OCz_SE!*1&| z2EoXczhettO3XU#lwL+9b?6ya(@tCiwR2D&F(AP?#QR2lyB5nI ze@LZt6ZuakbbH6UYc=k&153c@w&u2QN;t_g2Np-LN}Ih#nu5!FiI^w%+2;8VbQDh! zK=ZYW$o6i=;meUj?<`G>o)W6Z02CJiICS@!vvo_Kc}u`?y;?bdCf{4%@W=PCodUGc zo$a+8`Lm~I4_Cnc4Q>?r$LgevSaU~!!pHi9EKsP2-+8Q z|LfN`G@EH#?D>XmhDeCs#G$|0In6p!eFQU7J>q!_wiOu1+<*=G457m-XfCz%V3X&r?{DH|i9I$FB>A304RcD<@B!yk zpRoJjdxy=}7fX>0&bKa+`j`(8p#*O5RJmb9_;-t=LC%lyrjldJjL@2tm#PlrHj|>2 zgW78s47Ug&Ppn=&YU$Mc%cEpnXu9!rYV0X&rb|1?W+i|2!@cPWs+v&;m>535 zvCq#y^;eF3>8yUVOs92kwzV#skz|EW|U z38ujrBHLjVrz`!Eiaob?SBI^pD@_*qn5}XAhZWiq@1EA4d&)&nnpfKz$E7$_13+ZY z%!e>H1+uq;(>suW zq$%|%RYu#F_0oNAn*pgzgLX03egjJvkQ7wYWCzlg3E9VZ1K)`_f=KmsT|R@fcQj`b z^dP!A!Em`EsGlKLD#VbWIAkxyB8?TW*o5)>11p?DO~Qbwi72t2B7A@MCSt zAN;}eIca>W*yf)TxjkWIfRfUTc7kZCl4c3aU8OILDdsv-VGRT%9DyEE3nYe8Z&D z7J)IeVzgyd_#ODgOVw40jt;Hf8iPkQldO6)CG8C#o zF^SEa>Ut(s8>0r=V~=^li+T%l5nk4K(%d+)uaO1HD{q-C#!Cm3Foj)^*=QisQ7!Tu zonu;hb`5|a(GN>0DA>KV_0derOFN^JIFw3B4nk`agsgtCq<$X^x?}mhKYLPWm9Q4!%U1D)7FEG1J6M-r6_ld)95JcDV|6%K~Z*i*y~SJ7-g9Lw$Siq>y$>!$tY`5A z=X7(f2ri9Hu-#%Rcq2r-y~!?*eWa6SCa=(h<}xjMh#!v5$jM2#lf|Bosei1)&ygJ1 zAlnWKA>ouIB+oqR9*16<{Z-p*ZPQH^l$IA{D!LiH5@lOOCtmn~?yaq?qXO#KqrMPy z|AV3cCuyM}Dmc_1)261uUc*=<)Q`K2S`_z!UIVz#!1-GEYxjBb_zf>+cNjdPWAH|8 z6pw~XXtr(&zw`F;=!#})*%_f&AAv+F;;7`*amQra{D_=eq2EHo(ZC5_z`~E${bn3T zqH#xe*F~cHOX9|yErG17?N*bLblqKx6nQeJu^zE z9C}>t8O^D&hK|CNax$Jo)cVfFt_k)vxy=OV2v7pJ5cHw;f7M^(2QII)xjZy-I%^{ulCAiz= zq6X3l3oTJ;20aaNu@TWoN?6pBhUSqV@90i(j%%xl^NxKFT%xyiBVz}~B`=3>rr-tv zElVhbI!qegmg4wLuU^G(ua5-w`>TWfnVFn37LvonC&)&_&J1TN+b0i&RkY(z?<0O? zsxydSin7Z5j6gf2y~YxNhMl+Qq+*|o2bB@E){L{>U-A&OLVMenSc}B@yAKZ!DP|8O z5c25St=l3Vz?zRZaEde>?k0y{g3dK|xhc^k6cGw547yn32~Gn%%e@YUTuktJ3+z~+ z7T1sQ{j*8mcU%K8oey7@EE~Ov8?iMy2?(ypMq(XWoiJ#tq|pldaRZa|Bg4>EBK+(t zq5jNu|BLmP#^I!V$B=QO>Ymk!F=?b&PTW1rbj9eJUDBVm)jjSxrmBH_9*UxzyXuP? zY*lkP`SxEJ$lsx@M!IzDvhcyVO1>t{)k{&%ZuLpJq|hhZ$;1J-_`p|wyS*gyCt2u~ znF_AswrWJ)7BLv{ctjXhk&1>G@1EmPkfLLGmhKhv4|cV$g{dqfvs~*i>QuF6jr} zce+gchQxP z5gA4{{U$88Hhjr}Qu%3`v5za7DwQXE%CUYOQ{T=Cb7wG=^G_(Y451n6oE4Umy_>-k z9$C;`*i`hgLT+e>3ae_2TlrJsh!F0GGJ;{PK08KLz9~R}tI=kjx^;nMI%Ld7`Ym&) z)hcj|Y!_!K5a{qEkTHt2{z}D(L?u`J94gDTVE<4@OkEDFnFg98gme5UOkeH ze8obfLS#aQLsEUIM2nJb#dEZ6m$7G*>VeiZ0$ebO{)Pw9eP8OYMv{U}$XLizKcHHa zfq4Um&1#_kK}f{iiqIwbu-*xC+LgoLK?9n5ebV-))MCYPq+n8|Bd1MAd=3C_(G<5P zeJr(p>&KIzOoM%Wy4Z%ZErNgysaJAF?EFO6uIf?<@@wwYm zo)VOjdA0TT>$|q}W2iwUIzoXfvP+vej-JOWQef_E#khpCsn(OjTp|DM%^z%ln7p~= zh@YSc%3!`v&W&3}^~VLeRYQLRmbjXicD1UonW1c{^b@>0*XBq&knlT9 z`nlji{vaE%?Pz!Mmgj~iv#hB z+JIp(Xs_J#Cw*q84ApI;{#U~dD+G*cG8aU37sR@$gARul!Kv=AbEfG&mOmIU&HG!N zC7>vs&^8THHc2(r?>R@+dq0_P{8I3);1?Po;ozCuROaCzV3*kM9V8h^)X)AwygY9C zWAm;(E}C7HewI+1l#5X-L}*6MLP^;5=iU@M-(b>z0(W$My@>$ z^YqxfQF8kPn-AyvcLJVx(=n24zW)R=#1>XDTuAMMvunu!R}n0ZjQ-fmIO?{dXncRx+)oV|M%mSOw(IN>2*2PDY#5pA1<;2K&4*y<51T!k z2Sax@&_uLH95^ayO0{M3$d6(Njq$)0GG?nwU0nZ9B(?nx*s@95d z1+#v_ok@;4*irMHvr7&7lK`qgm-Z|kDg+F|ZFkoB_|;ib$G{_i=Nf6srCx8d%U1SL z(-Sv_e$hG0gmCO}$8c<$F55?5W$UX~(sf>G=4-u)&}cB7cv?irxp3?>@qOAt{-$ga0aKEpe4X-BxTkBWn0^e zHiw9+?W@S<2hzOaVCmGZJ37UBn0fdtnxy0Y3rT8dC|TFiu9 zox!r?AmOVJ%n{^rZ*yVVU@u_vKeTYw7ga+tL0&m(;8073hL5>q_6wT@mKv5Lzzge@ z@2`0}#hk7cQS|q6PEt4*!dGq8S~r1-dNW3mc!ITu)lJVlJws~hP6(D(0G@w?Y3 z>371asR!F~&5pKMW(sCFtx`pfn)FNOYt=CRk>ED6pAz0|jvj;6lYXabiijIDYt-bl1MY9j@bY_&hE%432X68_HhF032S2So5!jbtvc8(;Cu?L?6>)k|Fu< z+pw7wnepyP=Zr#d(`zl&MsrnkT81tW5GS|Wnx@lI&UJ>KV?EA^@#bgl`{(T%g9Zq1 zX9ZA~?8UWtdhG2E*x!yK$vEW{UV~VqHc$Sg{yWA1+MXApGVv#E~SsIa6#b2uf3*d?Tlo#P#uvHYounvzy&7;34u+YI_fJCDe1e zmxf3B+PGKx^w{u5h-0NjG-oWc=js!>N6z<__}%b@A;f7BqvuDT_`Yr4H%z&lKSHD&O^g#*dgp`ss7$8F^lF?$k4kS^`HhvK@i>5UDc9 zuAJVcf;BX7FwBbTugmpVs5pRtzz{xuvgL;U1-+GPlX0#&NwPD~1M!HVggj3KEgrOz zbSRWSVqws4IStbt=uDu4Q%vQ#`|CiwzM_G6ol)6^`FAt)eSL7pkDjut0{lWhs*=f0 zJHlg#|C-Tso+WQjm{~fJ*h9L5544v$^+n6j=FRfvNQXw(4~a#uBPmgn{kU*hnDt6` z^e6v7(QRtN$kFp;iOe)@C+TcVGkV#srdr zgd9Y$RM!4dPEWy7VCIoumY_KbZcg!Rp7?9ip1}bN)d;Bzw)T33o2oW^kE@vwZODvR zYyCkU2jl2>YylVBBMK66)&sHH0iWpPoaSc6 z48KRXpQ<22-~guLd78=gz)}>OBL>up((-g5Afw*pnf5vcmnWNGP&`&zUR&Y^ZtZ4U zN(~~5@bQW`KeDyMjcJ<7jnKnZSVQ-l|Lqi}I@pFyt}Pd<2&)ysxukk@1VJ8a^~&E8 z@oH(VB)fgrKJSnCoG-pHLjKGjm!_<$zA52}y3b2Yu%;@_B6sa??*}G38|MC)%zOYSo_%!ffvw4#CU~p?i*z$JtEwFbeZ|0L#FZK z$~<_Wx9J|{bFH^wxp5J>7>8>ixQqonT&U8sbIh6;2~#=hhO@FH&l=9or2TCGVfXD;-}M9}bdTq`@X zYmdtEdVdPzU~gx+CrD%vG6~$OA9X;FE)(=*U9Qjo9n|7f&W0-3`#x5@exYHEMUoLn z=;o;QI=A9GPDf)VuxWcIADZ+z{h9%}-#vz4^aj~ef@#@O-PnFLdDt8Wu7rgamVC!C zqe8*!#43ANrkfe*9ED-+ZYn&HIASzt&*XacGb$M}3dAPJL6cJ_ITvSh@vy5LP4aOl z^@5=sc_vp&H4Xdlz?_BmRBl*{5{oT^j!CO=u;r5u(*MhinGt(TWk#eYcF1{$iFYFOnemw<~&5wIZr%C-CorUddm^H!;=Z zvXw)os@>+*K99W7Qo%)k+6NR>?JE-cdh{BqZ@<-kS+jVd75T(!9!p%D@djnK4KVW= z8BUKmLPPw$BpiPC8#Kqy&d&QSr)O2(LG})%r$XoyZ}Cr_115LN725fJm8Gj*Y-8 zi@H+|wM*^lH6a0#J=eMCqurE*S+sno^2DKE=`_kc5l@THF%f7yT75KVeNq8?* zX2iZ8eMu;eBa(3jyu&B2vryQUX^pmuA|B*FKuDJjSx?9c5E+2#mr($x_;i;N)xkk6 zDueq&k^)bVov@OFwtB@H??VC{?ge?)+^~#DoFr8lYnD*L&$O{mvp>+MQy4KcMvyG{ z0~3898oW2~7Sg~~;b3>89@_RE+Q?`^q5D=;*>9pcs-Mp?Amwola6KIeZ2ih0!LOS zp9vtu(vf%i&{Y5K8Iu2a%dF z*6m*$4)>0IE|5@sc45Z;=emLR=ttG)qT>qOe^MJPuqR7MiM&|zbS`f=W=2_dB_}|2 z-f>neDc3QMx*L=qs9Y|$nIO)3Oj6;Oz#d9z5XiGxJej+#Qv`U8!A*81?MFP9!}m87 zu_}r1s`FCi4C4SIRSRtGhUWx7wyf)kK>FH*w4N++jWXYVLLw|YC(}u*1>TQ{g)U&` zM21iKjX_$+3_3ZwotmKAN`VBICSCEkQj)r{fxe=)O{43%s}yaic0tHWtycAe47Y*A z0pcb&`O3G|CgVoj&&ji-Lr!2yE`E$_mh%QFT*U02lmFwA0aexiX~cO`{wy+1`=eMn}6ACWz1J5vD3y z&tDc?taS%;R)OsbOVSslQ3MCk-4289&(_(`OQTGuP^0$-=v~b>OjQRfP&onubZkc5 zyem0ou3z+=9EOAtBY(IZD5b`cH+r3KBzP-^?Y)p0`h$-ty7u2Fr;P7fSQe~l=>vin zhs?3AkwtEP*>g~zX*9FAAe7TfVZhrw3Cn>|2%}4-%pW54zPYRES2lixdbu}Z$%ryo zT$Sgni3Lj>$xWMAa&mk;cI*IhV}QS|GItUb<@r3|GbwY3qrM2coYx%kwF$u`OQUz# zTw4O%QZuIaH2AT$n>_P1cX{f27lMIsim?L2$=I61$vbCferJaa zQt$)9kT}!jR;PTQv&Vz=jl(`0d)^^raBcnHW6Nj@Ly~iWb9kmftRe*(N!3UzZ2_xM zy1f4BkDOR#{Jys*I+S)dg*pp3r@oL{uz=`hL3RpjgsJ-l5~qGRRW~yheT@cg&%jtp zlw8So&tOdXQvue{(plm-qPLw z{n5dMAnQcVvyn+3vLzk|Q#izMZ%;H!|Dl}OmFv1wQ9q=*t)}*eY$1IAVmG9#DkVMf zbuKiNV-m-FD}@xvq7M^$6|D%Z{5`~+{q|quH}HyC3;_KC^TK%5?lM_ngeg~H&LMs4 zLOPgF9}DRA$o&j+IA-4iK3P~p-rC`Um1;^#asOSqmCaR%TtNrD=bX!Do!q5=w?>Qp zu9!l&FUWz=`Co21gWzH`Y!8MJ=U9|B=T>p5;O1ph zF}}J`k44iZxsNSuQWdLkN8gY0M<}Ca)GsNsjmJ^7S%w~Zwnn{jdHLr}8tw)aF|S{U zcrJQO?r+K9X{oH9qJPncHo%vOoBtM?i}aDlak(MbZMtp$jTmN1 zDy=(nw%!i~@9+?3C1qXYFv|VW63`sM$q7}TIa2pnA+Hxa=Mh!KXG%rwXCNlEeK{H3 zDzz!xQENUfE>bgd{gb^57jKC}mb1Ai+>_|nE!pPwsNWvCW19pT=UWaBSY`u2-z+>g z9loGu9{xd^GmXMj3wi*(rC7?u)QQXF1C9ofnH^_({=q|??HjjM8Cs#q{|<2-8*G7a zXUw+;PP@^GolPq22r8_3mtFf%%j)*p^wxMtAc43}WnFS5K5Vk#YQ& z<<2BnU%bHcYh$OkcqoA*_f17KveX&%re^PA-Pj&JyF={k4aFCQDQrG{n?+tzLOF7~ z$*gtjRwupBkD@Qn#wx&aKD8sj$LbznE#&0f{&}hn7b98cBb%k$X4Q`o7Uo23Z{imY z%b!&RUl;Wz@&kgp!KgfW0nO2y9qWdks@&KQF6*S~z-=uVGv;ZCbI}bdw6_H8Hi6VH zP!hKx;jY3#*;A<;LS%Zgr?vVn&U1QKXp@1u}9z-3?^_ zI)}#GILT-gn-(mdazn7FqhE*GmU}JrzSx*#;#BE3a2`|VE=V^dD2%sFE)Z2qJ*)ha1(8MU) zST*g2-&JaejaLXU0V~u_)H~rj#`K9lq)M;88Y-4{^LdwDji-3v%Q{kYXg~Q>2VIs{Gul95@_d&DGUP^b z1B}TOo>pK+#pAH0tYp@m3FWZUa895iv<{q&=ZMsRfi^^#{nsq(423gH>@k@xxH{=; z&=>68A8e!D7%CHs@r~V4b?pdkA=HH*gs9mjGkWNaJ8ZEfQarB(JN?l&)Kx>B_(lpkzZw3)y|fr0WTP;&liETL_<5XClhhZawipi<_TrLRCdB-vM7hPpMaz4;yHOw|2PNEXtRQV^(8*vnUOREW~Q)DI$GboSGMFE3K7MerzM>A!43jFw;iaGy31Lj**;Ut8YYWmW^!y-{O^BVfzgTf& zmr1-glp3{z_wJ&Zh)Vo3w1v%b0~%$&&`7+@7c;x&9WFSG_$bfAU$TrWqx}nI>K85e z$-muL8AaYiirwHDnA&)M`|UC8ic8lP*N3k!p^&wj_rZZ)dJ#!k^d^GA|~ zZSwZhRvZjeJObG_u4-D%v_c_yx9sF7d14#O`tQX^EmZ%IJ)d=0kbk1dr<2EhG;Fi- z5i94^W7ltMSPiXNMQTzzE@i9zz}xTU3_m6EI}AZ&Dd+)V1vM@_jLi&Y=Mo1KQ5CvJ zM4_#J+V|Imx7;E_WjC|#4^p1giq*cjkcny$C{jE&a9L}0@;{=h-siV%V zgixp4t@OGpWA0f_M^a=VpXiDnS#eS(F8^+6MGD5-XrZewEN*^U{p1IJoDMnf#d{_v z#Lpf-+4Zwd5ygDqi8{iV^grS?5NmPP56q>J0k)g^%F&mbqs(dYS{+e`ZmBkZp~7R4 z=%rJF`W2?)HB-?v=^ARvX)L>dE%z{EzMp$mf_!?BH<`70Iw;tI9Nd^N+oLRvFmaS3FQ4a8l+<^K^HI5qA#7uUFty`j8EX8N!BBy zmG=BiX}Sz8vc`pe>Spk%Yl~>Pwj^N4ou1aP&n#4x%j=MeWiaCU&x9EH!Dq8HuvfxVq1>qjp)1ztLOsM@c|~? zQBnkjG;Z2kaoX!b0VIfgDc4r9JmQ3fIh3)wPRi!Wjbv}U`xcT&Ij~j-3caBXi+yjv zQXQVopY1T^Py}=)Uk?tc--tx3iGKoQkqAdMjPT<#Mb;4ykzga1H?N0gtbaT|;P2v* z#oC8EGLUOkZ3*xLK?rsm^=uRF=}&4mh;_N)LH{Qe(5$z+d$w>E-v7yx)~5EpjV^48 z)ppoi74ejU-vIf=28{%<)tl&@F!-Qtgs*?ti|aP^A-NejKy;&)BtoPwU~t061N z?u$HhkseoDImb|nSI!r5PJ-9qx!04E*6ZBGbxGZsq5FdCxXRnP)4=E6@mK}vSv_;m zo!%rleN=N)J+Nw=9S)rIUiR`{4-qFkXdL~snJqzBHMG_7;_}hdqU5{%csznj`yY-% zy~VEea9ZiB9MD`zVNQxt%DZdc=I2dV(fYpElh21A)zAm^7VYzGn*pfJ<%*fSE}f9P zWrMSZ!d_;*2L;EaLc~=K>N}w)$}8^HmxC3Jx;3Be2%GZp9I=qGiq=yH-P%BnKP-DQ z1(k1|KN?$vIpmKIX^FJd)mdq1ME)c#zr#Q3i(L31ODWg$)xj_Rc0c1I9JxJaSVE!= zfn6`U;Ih?PU4NPSi1+Hb06;5>O2$;==>~L@HdjNvA$+S}f&;lE>{n-g73zvp!5ZPu zTawotPz9w)5~fOTp==d`W{w6P5PI5%-Px<Amg11k`Z4&HZN-nzTs^K7-_FYP%929jQqT? zN!3$6N7YUJ!sOPeonE4vN@8KNk{f;S!DR?3263!+?}o%71N1?mLfY8-y)0WGMp<2( znh|Hc|Q)7q{th%kFNu(hhd!^ zXFqGEOdHh9{!2-kIUG>`wv~_BRJ0IbcaKLGMd9SJ2_pEsV(DsA&DMhsT`12k5;I0? zPP%dEOixwWd`|#mQ0k5Y4UCoP2spmK-vVwILb!&?5B5g}WM14qhQJFx{&MfCdQ^z2oniv$XRK+jXkb`{tZ4JMNEt zPgq^#;$#67_8fF@gCTomE$Pn#xOmcXL1egOp!oD!R!qGrE#X}+7rqFpZDbM4>41l7 za?xdvm?PtrnA_7D@b~)3`Ts_)K-ISZ4kSe^%l+D2UyED8&Q*cOE!{CO=C(NjpJQBQ zR^hPiv~k-0W1r&E;eRUYBtRWN!u5~Ru>Kd!U7)IPMA!)0Ef)F3fRXFGLZY*ept2h8 z-~&ol?J!D2Pym&T+TJnV|M+-F16K?qj^mWzb%R@4NLY*Pwu{Rf-NWf#lANd%#(gSJ zv*u&-=+5`)?{8lou=qNSGHJ_a^4#=l8z~D88gc%uH~u3;SbHmdBw;!>(++gM!(_=2 zS;sj3vjN3Rn9M?h%H4kpcwAGHN&8NoR1=nZ&k|OyG90NiefF70;L~-4@ z5jL&O>=Q(8RM0AByPV2Zui9s6OmZ{gj@$C>3f_!^9!xULvYh z2MOITlKE38OQRg|?;;gU?lwEF_-Z!c zrdco1P~*~D!WXNe{Z_}3jBct-ytYOOK%0H)zX#xC)Y!WqHXAoA41oy*;r`#%CWGTK zxCUtGecfROE}I1tlt0MSM*(AFR2|ObPiDdxmM4eneHGUrXyecI6vHit*w4^1mS$r< zB@0R4RCSp&#}GrKge$4>=VB!XI<*Ip^1Dq)T!4GNQ*$>l{^Ov@(Sh0QYv(eRl%SfkC6bWRj5ews{(CNtCX+1<5HxbCg>dWU{r zzrO3b^}qfy06;SbqF_~73qOb9l)r;T;UQVA>KT{iq2EwK!GkkjIj51YXukhue+Avs zbkndr_U=yj3ks13o(%~LMDIx2xj`9Layif=Vp#mDuyqzPac< zh%ekSsR@QE)Zu#|@k!ewTkXjhJEh>+qIYGp=X}O1m(4k!TH;hBzi7VZHg$#5p@_fh zPcUyV;_{M%xW1?Nf>Ows30fqeeAseYE??)SN7KjB4qtw1!WC|_SU-XIq_VVq(&`Nt7ViOREAPAdRuzs{vZ4QT?dO zfdVT9Ol_axgw{jir<3xR8U`W=`lP0Z+A{6pM)#s=`S%{LuN}81w@X);+s<~(t> zMoZ+Mi+7~k(?d86{dns8PkG_IZ&4N2I)UI{9T;v8w4SVU19!J|+BrChMC@6dinEZf z6j*+M7)gTSJ(y5zs=@%W;%8>-+0(k}RQ1NyhC1v-C9bMn!d4cE+b33xYE z0TZ&VmHBFDRcuP7b5VM}?aj}#m8ZXs<7!DU_9L;MoG6#JCkKVn;@D)|>R4hJo}BF?;IwfVznkH7!W5D64GttbFksBZP=}N-Wq-SPt2rl_)f) zUZL$P$lC)KJwKUS>ev3tr3^eBH3|K&;9|NJH0l@JsTw$-wdzV_bjhX!5(s#}m&LAf zm#3MY)v4fqvZ-&JS|<+qWjJU#ik}@y@;WA=V0FZpWz zU-Fx&E?hN8bx{4tpLQOnRikO?j`^n^S(GU7T7n+F>1svF36r&EYN6hVq+q!HQq~J2 z+}$v7IeXLQ7fgbnrL6{>wXn=ov}>ta)3*xd7S0;L@3`Xfr72|)5=;GyKZO_SR55+5ZT77Oaeb5S+$@XaCd!!lhA|Os{bL_Rcv-L7*fN4?G zBO`jTE3p@+BuLr$(cN2{LgK-{qUM~0rAB;94v_JcBM&lMIb#&QOS@%bbRwSO%%@~} z$NwG+mNOO<>!D;tu0{csG9fefdL=-gYEC>lv*Vf{91us$$t-xGs5YX%cUvJ?R%Yn? zbOQ!@&nG2KwXtL9F4}+0D4zg1gtJtc@0q0&q+#@7Z~?bPn2nzx#e1>|mjVx*dU{z%{s^X(xi za7}`5AkTT4`tD|ff<;v)7WUq2kZss-Q@88AqHO%WZ#$M|8Bm-D{gm8R^icF{W?kfj zM`#5c->Fg$8wjxaG{#T_`2Z2<>&TP>lp59)o#0BrqjoSduGf%&sA@sPZZp%G;gwhQVWTUq?5F-S&Z zi?>N-uI>YtTl%|LWpQ_v=(2N1*%Sjc9kj@ghpW`EIm3tS+`tYB6N_UlRnqr*(sD=W z`*LLz@t)}}2TxxSI`99Rx04!qH?noCV znnRkfWs&8nD7oVO8k`@cNbGsc`YIT`gtYzDH84mtJi7z3USbPKtW;Zf9^GH`9^pD= z29VEKda^mg<`V+fcL7_kX1_DLm&TU+nt>h4G-Xp!(}hnn%Q&$EP6dcf)~zo060ba{ zElY;xYg@2Q>H#mEw*9q&UEIaU(qJ28W?D&ZTB;ECJ;fPCFyFTCo#0&^v>ry7>)Mq< z6P030&-qcr_xyEW124$V&~qM1BFywQ{-C>x4(`YwmY4;IFg!b89tU1V)XEzmb?0?+ z2X5Xwd8`gItG`ay;zv*BXjgz4aSoH8x0)Yin&6dvS`x%yUirb9prNs-=VmqXZ^9ig zQeeoR{(=KU{iwuCn19ZUgUdF1Z_ULf8IB^4b@_FggTN{LMKOBHv9!5@BHG9U@Jzn` z699fl4IMFK!Vru)`%#3Ot8APe2*8aT6hJ%y=lNy4Sbk; z#=VFN7z$1bRDnn~LM})4t)Hf$pCGLL!zaCebw4(M8WQFeQ{)`a@xd8$el@^ZPV{{) z27Whf(rl>`7w-$~R=&3uwwX;fO+;GZ;vXd3jfvnFQ%a2V*b4&mZ9h>b(l0g1tAO8oD^~c{4Fj~Cu$n)BdJ{$xj6f(7 zPBlP^-d0e@{Yz&zjEaFV%?@4;G#mtxrt23C-+q5QN=6*xD8=1Rz6D-EJ45kd$G;UY zf_jk4c#w|9y%UL={;!-E#`Zxy+U843-&c|{ya+Wl_CBgZ%B>BdA+bjlajtJv6&ea- zys(Z7u$v133c+OLfnHy>_*P~P1xn#*(Bh2#=<7&R;e!RnVOER`*}Txic^^UKnKmK+ zZR%)BMJ>klv>kzx%ITa&$jS1AlVw-wR1>XtCl6cUBUc)D)c4A8y!~#)%;&yn{eWnA zoypP!13lMlx@;@STdkTY7mKdJ#4Z>AycNea}SVkVt=-?f4#g&(w&)-8I! zCBeR~q$*rEh|~b~`qAETqsu>Xw|bD+Q7WRWC~A7rV}rBOJwjVY2dK2}N%`z}e8|L2 zR0y5SWo;gMg@x(&C_mak%P)>l`7O{hrPzVutod|@EOgaDsq!a9W*USR%_IT7*HO?sRXY#K1{3VO027BhggY)Guby`yA<1Tpp<-ylh=ssO75>0q__Y^@Nt5GWUYX| z60p)#q*8JR*ictP9&tOY3_R9QuJ}nFLLtmq4|2!V#rhR6Y)dxW$DP_eyp~g0aU%&r z0fa#ylp}g zh3+>u^RDT8t?LgJujo2Jr;O>Er00;riZv2}JzaLAHdkv_f7Y59a!^BXacyL}%B&+z zE1S}2>-Dfao);x_80j>9#>;CN3$bZmpRR{iVqeJ%c4Pd9fl+2i4_3!dGQiG0ed?&0 z@#7l9$@5ciTEuG5ND%t973j++B~eqQ1ajb}Cd}7vsD#vWH?F#*X+`?nX9%zIO{T3q zQZ3#+o%yaezvbncmsnXS(^LV+qdwkOjxv|mz2+4SxBPo)N7}Lj>R_#&Zxfh(wHYw= zPTRIos>Ssj$2lh?xCs}6p3XWhZYo{>ZoBiaNT)iz2q`&`;NEA!wD&AK-l0|>v$dWr zn)uia5??`>SAyErqkzV7IDetfNq+Qm~vQ1mHGx56K-QCLu>Q2(o7byiD+Y=Wy>qj=YEUAT%DA2j4;a|~F z=4r}O+x2hjcc7}Z(O#ZAvSFI}#-xtg%kZ_46Tb z(yP#7_jdC6T4`MAu}?`lPI-O4RF7%YeeELBl}FQh>ghpX*jq2w`;607-dyKpjh4`)l&M(rT+U+)M`iuAbsDtkx>ukOsFgW23rvGDYxi)q<&g zCw9&q4=3%?MPqF( zjmt~y{LOLgWJ|qGGF4W8{beTFK+OE;wLA8RbEj~TwW}&SH$>~R|`0x+9 zY%KBv5l@c7H^WM)s>M5c&g?eN|4O@N-r3&LYUhZ8^b%!5K9AcZXLfR>DD+ch*Hym8 z7(O``1)+mJ5KW){75hP=)6nw#CxOVP!^)@By%cWh@c2xKgaI;nF zgXYT(z3L`~?uC*x6_fwEhbs^`N=V_FtuF)QIIQ^{v5PI~ggebL^P^rR-c zAc#yN+2=XI=l2iH*aM+_VBRQ(Y{1$JuiY?R6)Snm-nO54pIP=V8{QSn>sfDM!)Yvj ziyLDOJbh39M@YZpZ$EP?>ND7OCR~7NUnz9ZZagM!w^fCS~Gllgd=OrJ_@znhAW0m9Hghnc*QE9L%Tjyh`FXeJ?A}qp4mKfxU#Lvs; zJb!XK>PpD#JP;TcPpzGCPi;az^5=n)1(m)8ced?0|HP*85#M#BF{>``m*zV(=B5)q z^dNko_kIj`8ST85v#AW8g9VQ8r;#Jvo445`cOHIx(Y0C9`fxuhu_Vyx3v1}+&-!VM zg0Bz_{|1yjaGWn(dqtF46z;*k1OpXW5ywjzAwl#l7UOs~)>Ij*XDsp{y{Ir6+{+^0 zTZ_x(0LKIr{1wtowT`Qa1ORCiIuA0Ma5k?&Ho-L6UAKSLhY*Z&asPa+kXF~6QWA?>?~x;n=3c2AC3 z59!N?ro?Ai|7NGvU!)4>6k1Mi#J%OhR@b@piBvS_BNk}CCU zeB^VhIxi+dA1y3niODKrqkgN)VghvCCo~;UEK)*dMQp0#q>PbSvVs$H`?acee)B0s zRBHC`Of}Yu&9@H-%bWO=`ru%922{1lEWrxf@-KR{HIJe+O+e*&$^RX}pEJW-fCEM@ z`lR^_x^C@eOUn0JYBy;!UsyD{%u_5`_^} z08g61(x^WwpT3chA!K}DKPhgjZ^(cjUUv+au=Tj>_Ji!j_clB~&i!8WPV1C7z!KcT}|bwLw->Q>p*| zXFcxs_PfGl4UUFS&6k1$+_fv>gCfbV2`1}Q8W_A=6!hC466~iOKPpQ_^6J=x<(o;& zc{Zut*Y58cfgz3qRiF~3y}MgbgD2p_1E>;hpB?Ix?;eU?_x~S=u=DW&Wa&Eks8}bZ zRvXjrlDg5FV_Hqy z8>d}>LG{@S2tD0NGWIP$KUlO3!-?EU>u>CMvTmH?4UQ<%Br)~w_;*maavx)i?mw?05NP;P%(OV5kH$>U2j(eVgwqXz$U% zFAH>)^l~8FTK_zsEI-L$=yoMsort}VXT(g(a>}xt< z6ls@Cxwb>YyQJ@Y9KGbXG$&g7c+foeH%8m{=^aV*y7ub?r3Ku(JiTwl&6COW-hA7D zVx`lh;N8$!ojZ`1zkBzN#I5tuy!-l^;^lcc%C}9djV}pe_R)bL$yCQNx-jd$ zILkpdW9$mJ==l1N97YwU4#BZMc%UBmD57Ma|5I1r;0l`cz9UQhDPN42^7ayx7>yJi zauqzp@CTU?S`3N2B!J`np4848GOK$q?3W7)E{C~EbX1;@3&nsQV-p{f8i##C%bpqv!3gYv$#sp*P4L)^t!FhN-(^2-toS+ z&y?sKob5Qvs{-B?0*E8$*}CW2RpuO|nWf{*MMEO2TYLQMe$V#;_woUgd*yedfY)VQ z7M8zaDW#NV;fF%_?fw35I6j%Gz9e`sxD&O9X<&2K%1K-al>yR7dG!_3OMSUNL3!b+ zBwz(B2`ad%prc{}!cRexg>cve$qTP)@U$(fH(_kN{L=O%COad-To_ zRR4GvIxmU>AvlHIF#|?{9cf~e*A7K_hs|TnWN^Y)4IiPKVkPHsPrxw!w>~q0j!Pon zQ)6U5BKEGh$B?GzRi9IsAs*RdP4*~97tCZ!(X3EbMlLov;_LnB2t%?vCP)+Zh7&)yE#IyV$&8w zZ}GCwL8T4JO!Ekd{hggTwe=@5!n%DtuJ>%T4tpFRdASHCVO{odooh^)dS@D;XAhsy zx-dg;7I?olK*^9Bkj^fWXZ)?c#k;EvEstn}$G~NH*Z0Zr zrJ68jSG~wq6{{apIs;k>+2^CK60_T!NdBdNf+2%?w6;ZCBDLNv5b8 z-cyS4gnIhVr^-3JYf`-r~!Mi)Kk*{QAqXr^RMRJ<#jeg$cU!3 zI23I6)*BhCo>4H6BHg}!p3qs5RXzPCX|RIS>KU6XtNL^z3U%Ckb*A(tGlA$9GP>tT zg|O^n{5QwB><`88z$GSFUMQrg{_%H>lJHRPgu+i!3)zA;_%u#6rg??wU462#v;uNQ zH_lqsV(K-)^IB+Odwm9&W-kMn`OOU5-0)e;diBn-A*>{Xzx;3Ft4CyKIb!X*v?D*?=Fs!$2eD=&cY0;WMM?lMrof2rN;s5cUZ?rB!?YvKYE#s&ZPt9&D( z$~VGXl~6l@lC_``WyFjMd*YpCe+V*Vy`H2Hf!m*V(MKXHb_%gGHm;cSeLi*V&>r(Q zepD4jFx@lshD!ZWa@+&u58BwNX8HP|HEwMIdlGEc=L|XnPw?)(CFA16b_dA9^z~XW zNTPOcBgAGaniO1o7kpc~_MmRB(3Aveknt8p;V%%3)p~iQtm*N==VPOr9B?KDQlx-5 zE}zJ;V6^)3w!Sn-V_+u|k9qF*jlATueh4TsNx)zQhB>v+mo$&`z5DUc3ycj50@q@o z5(JDU-gc^1e|503*_chqQMW(fJsve5jJyKQZr6l)$Ef$;xjR(maEXMXaCM@$rWQE! zlPx;FWIB}FN3Uy%V5ofl{%flP8mdn}23xS~XLirjZQzJ{!4$v{YVwgir%>FGZ0m|@ zBUgzjQwLBlY0}X(Z>3diwg)QMwxAzMr2}0g2(hJIiTVbM5?qM?mXf6>lJc*+RNQtS&!_tcwuc|+w|K>(+19X1je1Zk#U|d~9oH`1 zq9^AzIqNd8Gy1|;>6dfC3^X!J&q?=%wP#XI(9(_zgb9vKn+3(-6cEh^THjkjL>TC2 zJzN)w2T|98VX}VQ4{uXbD9onF*yB+b#+k+-Ozap3^%8a4N|1jl_wg*>MyXk!@YS z)cFl+ zTi*@S(%BKLjJhjSOrN&kJu;4m1o^y|6t)50(#itbc0V){Bn%+Kpsq`{8q_ zT_*|RbZ%M~)&e2M;4cdPQzHUG1}wX?k7qh$+F~uTI0*vdAG*DN*!7{7>%u^an7q{w zLh`a}CM-q?Ct}XY6+)WN`xuh0e|o9iJ~Wetci0ob^fi9!4mm0oLA$@2uj0^dh_0ql z=NC%-SW)Efsa(O?*M8UQFv6y{cLVtwaS+m@^!RbACBp`*wz#RgHs1ZXF>kD86?)T7 zmL!pvSL!JFhQbWay>BqW-pEooOyYaojJ?S!Nf0wA!c+eSG~O2I$brWVeiu&-?zZ8X z{W+c&t~ux)(gPXP5rm8>WaCUBhkJb<#$Lf;OMa61os#PnDOqSPyM{=k8O_ViUK#i{ z#*ixJ&#sr3-}dI3J(!7`Co*8xAuyeJ+%65f4~m%-ORt#WY7=GiAnQ!T2R<10Vf9_g zUYN?}kF*KjaNKtYEpLRO3=WazTccErtbS-k zcz(Ro<}8x;EmAWMb=U=SUY6G^r%zWVwRS6KX;IG^Ntn@+n$Ge(4s;SUq(!^EI=yRw zgh*@rS>mtEk)u*&Kel$=@3j23B|g^US-ep=zt zK1Of+!>^ALyo_uxp$%57y?6O~jO{LcCgGAOjxPh>^_O{l!jt}zsA2&}hVoTVcD+c^ z%W)j$604&7gwhJ8a}4#eX*~mHYKS3EFT%Xuaf4vqN|~Z9gsVDp)>*K9n$*3lw)qgx z$7|_Yx>11#$Ejc$8b@_!pe{Ie?p%S;1K%gIriyw}O0q?mDZQ)->SbV(0rMAXd>B{M zNSFA58`9h7qzz%e7wo^2bd><^!mRhxQLx!L;xIy~R>wx&kBcssQtb^h!o)DQ1F|a> zo)PM{x8g*7>5k-E<-N$nnCJ~T9{d5uyF4LJynP8zT1Rfd+m9kOlai~Dz1iQ$LAFOd z`@-&`KVZ`?as$TgJ}9Ik8e=9HqyK8(aL4Nupo>6K3SuXgr3^JXiiGWWT_D(V%<+g9MTYk@+NMJM@$r&ADwR{lgv}gf|T@I6g8B&HB+pX7p=&fP&9M&P{)-zx0uv8mB_pbShpIvM4NC zl#DA{AXWZQtyDo)$V2=va}ePoy}h%wj(w?9W|ZMh2<-*gRPdY<7iKP}CPR*!(S;5k z>2^02yw%(`h2Qp+T!xXEq^t@KoS}B#QG~*Zl_7Q;k_y!cBI;nA8S;@q9ThIuMckI? zU~jyF*;C-2ZG~2>P)PF-3gL{w+@!+9eSqy%ZrKE^m5*k5-q?Gp5wXW4SvKKw44;%| z_rkljj;M6BXuc7eJ5y=0qqJ=pBPdW~kU*80SNS?&j;9 zdTYlRM-3`m9q@|%U26&H1R+vMd{qAM*TJi<)$PpixVF5%GSF+xQR9581I=Ud3k)q2 zt}JPjqURK=Nk83d;SnE(YkuxszkMw}gpK>2D!sAS3Oy{0zmj{08V18=^Xi;q)utc3 z9c`*ZyUbzCyQJ!Zq;=3*Dscm0U@5zwUFerxTFf~5{Q+7ayJYiAL1brdv&Nk8 zF@`3p*qgCtA)F8voM@PujyrLV1wcrdHl%$x`(*!j;n+* z8@~5&Gscx(g5@9n>(+-Qvn^KaSbL#GqB78Xnf~s&Klz|H zhIV>dzL%fDBcJ7*-)-L#d>BdUv~r~x$?al`rz6Q|5;gy6glKbnQ^)|_QE69ekl?TR z-F@e_Uog@P|JGFO@9>h+T{{JX)I6n|+UO@HPW8X^vthJJqvjHH4|Vb4YG`5p>R%t2 z5AGnQ3sDAMQBgP$m7p^D&$E6k<(l$&4aSEF+tO0sLoBNka*^zOcsWeHd&m^+6~u~E z5_M(bkn{u=maGc~y4~3lJshun{MEYl5GSm)6@*b?`l*k26uVYyObcFUd@zolMx^hR zNs3asE4k#&P(1oXCPv!s(0&nC4GW>#3B%}hY0dv0sf24Egx4F^vwH1kkzv2&yKUbN z3f-&Jdh-X*KH3BQwz(eo?LiT7^_s5t96J^wQWIoAIp86SE9>Dg$rSa{iR&`<6U=dv zSuqpTV2Ti#Bf$}Av++TQ>kcHnv`=>VT z@0j_Lm8MVv(rKmHXGAT6&Ea?{tlRn}A%Wx%YOr3Id0 zjTIWte9NW^--dM)*1xa=&0Psu1ocMYt-NQpycU4l(N_K<5E~<@xA4|)7UBc4x%w)3 zkL*vPCOYvaFw){8SEL#eQR%zd_ON=E2sx%%Y4h8@6jXheHREYc@^ydC{!W#Ii}DG$$lC7G8+T0b5@GsLIJNO6dhT$J&l%+54FazkIu z0H1K^gy78{W8K@Q?AKDd_Fg=C=AhdnX(TVY5_gm^M1Ai7@|J)~0A(-JiF)2IH=Yf5 z*}S#MhwJlJwt{yE1z?rz)|_j34`X0{+8k|cxyhh(X8% zd!0w?#qS?lN7*7tgi!Zk?GNO+^931ygl2F!D`p~r#=Ykh_z6ce4NwU;+$zV|VR&}W z?*?y)ZXkv4+t>}K`6p*v#!&EVpB+Y|7Fpk(c#T7bJga|CcPNJgMofjEVGE%v{3~2v zNYZI2tF6y;+L-71gTWPK(znCpML_guBq*I&f;D|l<^h#Z40&J(#~#TRVIEFe zDjST-+r&Iimv#E1e@*7w0Mr{lB6S{u^6QMgmQa#f?teV0 zm#eE@K}DVwK?`p|+w>u>qb_uJUzQ|XW3e6B9f!;%a^`oN*AQya$au=8Udl!tn6(s! zfurs$BsjCj0z*QL(VhpvdKDXDcj>f%G?pDO5AYB zaF;F^#tK5QWJIyK*}bUK^NPzoe)|)J1Xq5eux#9*!(MU3AZ;2M;%!H{HUtIrcO}JM zzrXJpBu0@|7u$8nH3}oPFk)knI6?CZf7~NCYcF(6#k&7t%17pNY2}&X*_s+^bUGJ6EvpGr%MCI$B&QqL({SN(Q%v?^8VD3l zG#qXd>rrtw(V*k$M+n9@2v0xem!#~@)l9y?;^|@2r@O(l|e< zd`ACJ=!w#3cG0P$GD)s@yOT8NL~!p^ILB~aV&)apo~tO75$Wi&h+yB#zVbD$R11K( zrErP@>3tVx+`~IZFFxEXgCQmqdN<@UvCf$4D;&C3Tiz1EB{-zHX>kUFpC1CmLA}0X z+WQX!6J`&&1)AZu4vf-z$!?FSv?5j1k-FTXeCMb&eQM1lj5&}Q99};7hi{%Sv!nJ= zMnv=j32vz}V|uTO9Xbs{dg`nrorV2aa@<@~$mVDq#*V#kQb3O)_EoZe4IeiQlI_2N zJchYV+QnyDCDWYUUi-d&>oI#TL{*Q*xqhbq*-*;lFQY8l`UjtSo>{ygHJn%BoCmjO zuS0-1tBZY@dZw0CRdn6IAvN)y<&lXaBQw|%a=Oedw;Hd^BVR~StTd-nOWdQGkzu3* z^u$;c3m4~}Gb3JaA)o);5-CpQabDI%a5Y=OFdAR+x+rt@2$8q9)B|8M^ z*pi&RiVcB?ziB_|YE^c-PgIn`d->RFQx)N}WPjIWKWVdZ?e;rSszej~Qq+tpZTn33 zoBT;_y-f(0-5tjq?c!m_wL}on;>UxkqGoVV`&~xfqkglu(DK5~lH}P-kGoyaxG-wg zUA_I$Dg5L#Q;s_D^R@3Xb(+ez=M~8Z+dXOO`$XZpos_|gtC-Wk8sDYq$LzZwdG{=& zh=$u;PtCI*Ao%nqPV#AckLkHrl1nnCWll2u5@C)w(f!Z~=Jj5419;?23K40dUoRmh zy7&U;kVgnLJa^?vnwSbdb@19-@=0Ofl1KA-C>zv53zATl<;W@bnKC?u^Q|#Eq>i4d6E9MfL)25 z%4Wo8f_@*~cWBAGg&+`GJ`KuASpA#B3tQ~l((ZPe^LilW{TEDomfer5^(Uiac~xg$ zk3SkbmmX=Q^8M3fd-p5xy=DLV(CgA zsp9sbx)$u$xO2l2*W7s3geZh2o=WCY z1wDbB!FS`6HSh~g(WM;3+k(gYf*&>zeeUE)!jCt>ml@OYur2!wXx^ivZnr_-S|l4v4&9mZfARMv-fqqPjlGeDC3GP?`Yn$aG(EDJ5RrZF*VN*KCfhYUgQv@ z)?~hwE9-4&64jAbz&<1KIBM8aW;%)wSn}EQDQ2faHSwd{TAJzK1nA~f*EZ8L6Yx_I z1$nfTMd~FM&Qkt=vrjlJecs{<--0}BHAJTzDO**+;6>%ETz{gDuxH93G)}PI`+LH~MxQQ7 zoJ(r>A)%I-?0Nbid-EFIVs%0=e7jzynegq}utep~7{Ue=%tvV3m~VnLD}5u_^>}O{ zRa|nS8+@gkQ!!d#9dCYqZ^s{iz+5T$VBW--!VAN04yoa|RMGH@kT)5By@MVs_lC&9 zHX9c@qLImf!_i&w?%S9X`7HCkB5hyof?Froo`UaaxIubs8UO73x}T|^idrG;E<8)^ zLhk00`WwVSiMOEV`qhiSSrNQ0@XKj<>`q!UT~76L9r?uclS{Xm^V9v3%7lQ1bjRli zyrK*UEyD4}d0z2muf=@k~Q13WiDNZ0;iWc_W0|c zTBie5?=$>bo>r!VbQW1^>nn?c5raI=?$OeMU?@@_H5@`c#>t+qKQou+XfWZ^J&mSe z&{b;AXq~;H3{vMS%c`ya!Bpi$Xxe?dVal^>U^e!<=kq}b>bwRiV+R67XX@^+8`a{R zxx4)?;=-mE@^}}j7T6~iaHbAQ15Q?smVivYC|A9zIUW##P4Mn|BjnX`R4NYNdOX}b zZ@>>~;A85G8l19^fJ8QKMZXA?7V)Wko&Ruqm7L21llz}w)b_HfMN2kJ=_Ol-jb%>72(jy8}zsGf4 zRk@^r70iNl3>a8~{IAN^{tpTwiiYrU`a6JI#E+^R|H_DWb!yE<;doyi=^a}2#4?4ghkW{M;uX!u;Nk7|G{lwy+$$qVo2$* zM*>XCjMlIY(wCPyU_CC{Vy`ekvgv)w-zogmuxY2eZ;Ri6Oqo4PClR-J zKS58C7=6AojC$>54W8YZ!!W1|hKQdg1(k+P`U!91%H4Fr;zd8PU5A=$n$>r6-SwbI z%m_}alI8vfutCtHn20nJH=4a`3=bV6L?hkUD;jndwA9kT+rtUtr+l|l-dzZ+`Wyzz zSOyVdnagYM_`_+dW@F{re9!X}$Q*%_Pu&#dQLi!FKG2~X=B8LAskr+?vi1@8$`Y8@ zTyvOVn`VIgr*wwEMEhCLzF}cK>iTiGDvK+!QLi{|%o)JD(1|uHie+;3Q@6EAwd0K& znZ=5r;E8wOyj4>_IWfWlP8asq`~ibtPyAZ4wDw9z4a_EtQHu8vJ9lx{-u=*CpP&)} zpxm)vIQx$qg1}aY*Cl>Wg-?RvaqxNt%Nux-Q}SZX4k5R`&bw$NgMyLZ$HQ(Q*xn71 zN!yzQVzY=n{$b&EnyEK3|6oXNaaOs`|Md_WaThv1vl*nR%Y&ECa&ngVBEbJU%}XO= z_RPw{Z?S!+{Wo|u^{oEC+g9o8Nhwy~Wqr{6@E@K+3Ae6(`lWfYil2Rn1x)ioOY%yK zQme`}hc2Y{nQC2?AX*^)F%04J}>;`cN%%D35 z1@D8BID)N%_H2U|!!$P7g$cqm9Nw!4@G^E^jCZqjcam^)m?l5e0>58%2Ry+pl+N>> zaL+T;f(@=898{;=lq9Za6ULH>64tJPV+d%}=qcCj57%^;=bGThn)MBVdF=n<9sj~=Ifx&WG8 zyE;PlQIB=+(@q3zCMmZvaDy|;P*Yv=ww9DB3t21mRwCjNiRBzhOURd7*_A=qR^n4EvSb!-Wx=G3MK)2sRXtEB3>C{JhdIdswb1Tt;7wN7-lpfeVY-U3eLX z)H4VjQCC!TzYE5>efj+^{QPmDOyYN@7SO8X4SVY21;+W4PtDv-ZdgstCeeG*WMA@bkFt+QiRIY*Q_UShwi^8}3!K^1N zw|~K$`mP@+(D}g7nfBeQ4Y%POy0)ha&S$MM62_MmiXo^Bc@h#Ttv(v5rKUcyy!~0f zNGRW;+wZ09@KcKUsBaD3`d3)`buCCnP?x@e5rh}0q2$`G!9R{)*wVPTR!Oz};Q6>Ee&TM&|!4xP(7 zW_ESf7fx`0p*4Ok|Bp_(V#n5qKpSJ%`L~VPHfR9;dj1m%8N>V1IAdR5DHlgXqd7me z5GN$iRH}pZKkA%>);i@h>;EV9R~L^=(ZgIWMh7GQi*TN9Z*>7nhzam)IyKWr71F2MpwtP|c& zb{+1{0YyG@y>M?8R~DQ13HDqH%R`3?FsM4cR6IS(04X7(l9Zv!kvkjydl1A$ghshw z_quQZ^F^7Eu%6&@h?|5;$a-5mvJ#fp1-6W8c`oE$;u}APjou%$xn5;jjt|nLBJ&{sO6v&BRewh)Zly12cxz4K^z$>jF(vuO{SEt za0_)Ye~UpEd}n$+a;l&e+qA_sVA_Il!x1&bw|{wwO~S`}&m(EY=&up5Nw$L7BZdeZ z7mG^sG)4qi<}WGa_L*wF2iy-vH-#0tQx&}N|Hx?!sMETJ2>z(^(??2n=9vcdxKq8JQbpzHc9l66#1dhEtFT$?HvH-pNxK}lq8c4#aw*G;`jn0NG=90KY~ z4OA_;L$Ghg3h{SJ#*t9|#PDQB+tN*sWwU%@)EZ-NA_}tm%oBNTGP=R7p2xx)PTT$KiaWQ{O&>5d`3FGLQ**b zX>-cZ%8~~nv4j!g%#JPHx&BhoWND0Wiuf{bozUuWb20&YTQ{Ac=TE%0)@9rC80v2m z5TsEB(dBpv&9*oHkWTrUh*qpSNBQGa;MnvlVY~^Q{P)1TAgHL}&1d_D@_lzGrS}Mb z9lS>OKMqf7RFD6N1WK0A9+oW>oyGzq`xoZ4M%Q>|l(qAwe}1hM!iKVx>j=Y+Lh@s* z_b*E7?}5GN_p5rvHQuza0w(SoMs)G-#7#!8mD=Y|#b$KWCc*Hmx2DY=UUcM09 z<)bS%_r4=94f$jvc9+TRrVBjc4l#XL``PFQ5nvVBM;{D%jbbp4s7DbrQtT`NCwGSm zxdshhx6WvQDrE*^hG7d;)_cNLm**3v37nIbCHST)$4$m+T>!M*JG1+~#&lF!`ejCT z(UNB5-Ck|>burDyigrnBNs&25F9`#YyVh85qmbfp=v-mJPzVNRPFFgPv4ie<(9pFrPa9Y}11jfo~X0K|0f*YK{7)hrSr58}V04-(mv&tdi>iYr;*%XcdvhKL?^ z>C}3(_t3dU50^joYIYeEK&!UmX*suXSlub(9g0hE`yD_KyDOM*{OcIeYC>w!p=t$P z|BeOq?@#p#2A?&eEIfG7%@evgI3!G;Li6AXFzV;qgvmvxRS}dh%~b8J^I>XU6l|*0 ze@rKDf2_qi@UCt(4$9bfu}{m;?A@u8<2bL=eqZ=Acw^4~ZZ zLvKEFU5`55cM|Mcy6kAPEes1qY|Sq{@3Ayd?a(zXItFkf_4-QQ10uCw37;`)Bw{f6 ze98HMw|SwtnYPozocn$7G0)c9))w~o)aL)o`>Tz2-pZ0~9vJ$3YHMTLR_5>5W_vHX z{+K11H54o_20>FRM`)f{79!nfurXN%+LvV~{9YXfB(#GRADn*w`Uv8RuE%CwY%O{^ z!WjJPWw+1(@q|qZW;G+~vNmy9qy-F;-c!G&_KBFFOS$#M z?Qo|d*1(%k8fg}t;FHp&%)`73zLm=nxgc%+1t@odZ>;a#xJiUlLTYpMbo;q1tQRL} z<^(e&_zRaw(XZyOP&y$hQLk09mI@=uuB0SScA=?OxBdQ)Phoip=7h>iT1gxiX{J}& z^_TDwoafqByt|Qz6BI-f3)VU98^2oQYImUaxS%%bCl2P|g^m&fC0Rg9sFDvC;^0TM zNmi359{u3m3KQ#%3t;oey<+Xewq8fGG7S~HvnUOA52in2e3!)$6OcCeaN?GdE;b3> zh*TR3A!Vyig+{s0Lq8R}+f9wB++4X;Tia-^HCqyX`oT-_C(abPit^}pyVU#1g6KW`bQ;BAn)A?r zJ_YkBq0BBLJ)5HYEYh5&_y?gz9I_cOi)uixvtV$04kyQDCStV%V;?`_)}wYx^0zK64+~jhGERr= zh({KRkQ$VpLD8*R_>D02H)VP_44%ak>p5{awV)QDA!}wD*2qLiD@JK01l8jK(-*U! zu=9|G!DJ-{l3fJw@l>&PH%X!_K#~-FJpn1o{ed~00`5Cyux29nKv%J@zo=UfTNrcp zr#0akS~`42CtRH~r+zch{zjvDXY)jq>@xDbkm5M|4Rxx|l_nc{!|$0)&JhMoQ&dH- zBt@6P{}k*6EeZX(KSxwO+CcAhFI|tCoNcYV`&2_ioNFR?iQQn|hgDWf3eTew>x5;x zZQ>1=zeEsde?901k+h^2TjSi+BOh*qcPCv;NL(m^X$=Kl{jWY^DaN^Q|Jexg2^ybd zd=Nn`_X}vB09AA4`8aZ{o<&&tU_(de)1;_ud2aXwPTP+KnrE+f1U2%ACq20zCr)uq zCH}+{^wmD;<$6_9-^_E)9|S*K-db{*9&)MX#v~+|!?}ri8@aHlmWF#RZ|}CUiXFPb zyg<;YGxKGcr5_GmN6DqI_Crf*e(Hy1<2~_ab_q$t;gg^6XEnj*%)8cD?L%qGyfX9F z9X~BS)7(y2*y`VOY->_W-V2ySOh|$F+YKaR|1T_#Wcc70t0Wp0ni^6iq&Ybm4ZS^> zn^UUO6W`XoTI398Nnb$HY#iY#6F!k#WoHRoWwx(*`(P;+{8<`0mb^RM``OgF4?a7; zd;x&7g+4zJ7fojRs~{ijt=ix#iWrp%DmxI4Sk3oy9bdv8F?lEHcB@PV99-3w-b5S&JO*WN@-*AIrpWTq12nC=?#jh*4+Tb?CIqal>u3TW!vI1j3>NmGc`VP z4I!|dakxs-DO;LBMYBLK9jbr#WZMS@Pqg{B1B~KP%-#f}oOvkpTJ_0V%#2f~B4_tp zoW6msD(EKEZ3p)>xe+`DB~E-QS#0{XzREk3v2meZ{R^;fyWd`Uue;;cyZc(|{|frM z)cwGkA={nftan;)Y(hMx4ein+L>KXhu2B^!j1v5G>^xbY5FpIi70VUpB=)$k2HHiDI1BOU9$yS~H6Jm)Uo(%DleSp0rA0k})o1#wGA*Q2Pne?NVF7-@`NYR1BUqpz^y#(JpY+ffGX0nG zp(f-COB_*(k@|e9>uT4s9=F_(GD{l{BC_se6as7wjta5O+6Qy-?t^Tm8gI1e5b@5Y zMwOXzsv?bb=f8lhH;^?>;OkS}2$x@P4BuW`(DvI@f+JQ3%>7B!KwQR~u3kSdD5l63 z-=tFc#(ej`lRaP_!whGZF*()~Bj%xef0*x@42NFz+_gAj*~-#J%3_Ai-0M^y}A-xc+QhYj`AE>ryVC=HM~obyDWR;RL{3#T=-C8je*+U z{yU(GPj@!dk80zl>qem^%ehSDo1f zLzX+X@U1fP`3EKb6z4igEs=``q?z-1@m z8-*dXI)t1+#b%c3FO~5Sf#Q|clxAI>Qd2hq=POq6+H9B2CNdIgy z4jrN(51+bog4CM2Ia&$6P1Tmdm>1v?rY5ATxaJwaS<33YOA5Sdar>D3hmlho8$Wy6 z$g*4H`On?5Ao`@akILFTrWld;zgT&*y%%ojLge3bV4o7qehWiSzuu|Q$IiD1r!;>mdr%@d=paEc67Sekx$=zpLxsCfiWPfUG_l<6e`59nqV%ogumTjXW#3Q^DR9r|3`I(I|BDI_y4-F-r&P!F07z>X zbZWcf@z@1)b>C#bWP1H({_+CUN#THtwmk<2`=9)nI?Z&b?lqNI-YE-rDYPEEhk8dp zYArrh-JuvCgaQilyfPoUPlcy5+T$mdh`avEoPg?$*P3km<$|VpGh%;Ew_)0od=iMC zEI(V#t{}6Zj8f!Hj_?hOaB}wZ398S|Hd~Ri)b+D5ER0NEGYSFg1CZ;F2ssr;$b37! zXto@2NXWq170c5q?#WrHN`j^};!aqi*IrZ)-y?q7M7&<5?fpgOn9oBaX-@-KqT5=V znyFi3D;#h7>-T7oQXe8#zU=O6SizBE>Flw@o~ua(?;hxmW!*zFSSnY z6PY0i;jjSxAw%P7CK;r8@;)kwi+@Dd`ZWf~TEh)udZ{>#AI|9yUS`JjJ-G=EW{f?I zbD18Nd~(yj-%#bu?|1KkZM!PAx%!k7i(gsR_&*Yr2<|AT`6aXMciV5=03_iEYG}f# zJ%Zc)bkKU(%)U4ZD)l&*{(R{5H78(&nEL7LIgt?nxN9E1f?c%S7F`Q< zLjc9Oub|AynVhBb)^qBRl8c2FD6AG=n6dbIm^n{AizL=<@nag%GWnRp%8r0K@j!Zf zp7fY+TsHVPj$Ums-rI~=iQSLWZ^zjczpshFTVZm6W>HwEpLMyUbj(s{_W@>IYl&Cup^kBYzp>Bgle`0T?yZ( z$!ZCz2$GxYmj8*9c8MSbQoA|8&i=SAzk@~WmvB>F3oQAE2oAR0)&Dg*s6^zdEqdz5 zazn!xgD9)-JXz83TJqWFn-~gq(8ZfDvcZQfo1e2|J`- zJ_`AD(ULlx0gfSmvOf!sjoL$@2k}^~>?K!Rb|biIfp*eyh?5Jedf&|LUYQ6DP`FZ? zY!_E7fF-$*!kO2JxV3|3+6M3%CBDb5{iXl>1t3vxkQ^sL->QI&0AZ{mqyWk*GzHyYno2+iKSDt-sG^NjBDuQo(_NZ zt2crOwTZqjhb>~8GY_PpSw{t;aWelEm{Y6W=c&PP7Xt zfit^ia$YYfN@yNUrxG!I=L7>&3|Fa++$@SB;{woQN@){@m3@LXBC%WT?)qr!H^|DV z#|{SfDZSTd$DKZE3<`f&e*H$<@O0w={QM;R^0W7ZOF|yO0hVfA!Bi`dP^;_aX1wcY z+=}#?L^%a`rGagW=jcR~L;RXl_U*x8jW$Yx0NTJo3NTdV?Ycpfny&paEi{?Ms? zn^V|%+jORD;JX8N$Nap%y*n&ZWq(FnlLG6uXU1SBP8kIGw(VS1o82c>r&j|7GRl|_$>m7_zv?&XIS0&QSJ}E(4#QCG z3BQ0p-#CV7GewC0)9NhxP1K^}(y<52He2%Usr$Nw?Epjs)PD8@EL?fIO85 zvmdi3vyXyKtDQm=)-)%@7e74M;7#r08O~#3q-L~MX&$oU*O%kfc}v$6krlX-mucTG z#C(+IDYiyDENbw=p-#=~@A{(S+$G^|c}vrIrdhPrGkPmL=?MY7A85A#77viDL-!<@ zwYRcSR>%#3Sh^cdhP~MK%cDPpz=NCUC#j|T54UrG$_rrz>y!_*4B6y=DUf20H9~#R znqj01%R}a^n{%BRU_M;Xo2tY@^Q`$6)f!tr*Gm6DduCN-jfJW&7lwIn82G1X0o??* z-sXPJx#W`J-`C?(EwR;Prgk{8z4=Jy+>==WQ3&zhT|1;9ms6D0lG%3TiO_;aB)Kqw zP=56=*t8|L=At~PW8lbmOHdQCqk%@@s5%Pz33 zi{6D@fI%LC)fiX=Q@^lYxxzTl^D1zoh}P`e7%;xbb7_?UI@$p`i#NXW$XxjD$zEi- zGINA8cOwMaYBOpa(>+?Mi8odeA8`C!)A{qZpuUmdp;DEZ9FXNG$cYyc>ir$?cCo4@ zdqZwuM9Ign_r4=|+G<1cuK{&rumYs&vbta5{*VaSUOGsh z*0$e_wQn;MJF{lLdb}6uN-LgKVaMwdq3laT+Wr$mj#Yj|v`<#Z|J&2Cir^%_-g0db zBt=Hr?-Ajn_D4p#y(3%)J@US`CkuQlyK({^0f6P5`fl1fc9?=b0!0VY2Jms(4_3{a z49qGD2cZR$-N837$m1GslWyE87T8vthw$hjLIJ?Km36rt>i?zRMAP{OJijAI zm_-JGcf6@l2&4v7;|ufzT4uXGW+1eDD*YvMRZP0IH}CHFYNsNw0*9t=|M*-XP(dwi zX4@`c-ZmD8Mq(DzYjkS8fY4Z!dlF}xDe$ELkM&e{=iNwQ@3Y8N$jy5b`aQ}jh9w!s zN0W{63Y<3SjHvVomZ#5bL*WDX|^bhjblPjCb)#utR^9{W4h~3d z@FyQfN5SH_xPw_c)H-FspBFJESq%k-CV*mG8KxgzopHbHSZJ6v(uJZJ*6vS-squ!i||N(pt?}nDzK!_>r{0PCMOgUV)wj1L$;$4tdSaOvxw&HMdP5$*JW1R&v&yv?X9VMn_ z|87j`=Hi`Md!!}{_{88BB@T;_sqx>TS#-^d$LYZ8NQ^G2GPe}T7K8>>$H=Z>(5~*! zI}fK?;?Zd-|49cEUdvAIFf95hOS;Ck{p|wow?SITTYWg34m|aE7dCwHsL+n}>)e~q z=fBCjRvD)JZbJ(Z!gfK2%|Q(!f_Z>|#16#lBrO1S#h`2LYu261vsWgkTjsd*kEx$a zHB3xBKbDU1$H=69jp;VQi!v+0cjC z9De(kt<0mEaNuK@r(ffX|GI`B*X(fYc zalrldlM@8_ZOMRHY}IPZJwRLu|3}*aFZY2Wct8As@3JQ#RP&s0*5#2 z(wAb+q!0B^0!W2Y1B4aN#1t(=n-e+N4ag0~UgBv_?>S9|Tj5ap`wX@^jlKEa>Cx5f zl{9}JbV<=fvwPP6udcOT3yYH$IBVTGHFW80$~0XXf|;JZvOlYZ^wn zl0B{k-mG7UE07tSck+(!v1xqbjwPOx=(-=i5JUqDKlm0hf|U^+q?(!1F+Q>2Jiv4d zBtpj_&2W@>XRx1Y54J0k6$GtOJ-mw1OgZl#!+Ht69Pd6_EZy>4s_mWdOu0AG+#_di zzt2?-R-GV^Fqim^b$knn8@M@3Ix}+Ve*D@jV6C;}qOBKi#}0zZCebdT*Jg)D*dMs2 z^ZJkLy%XjGLG*&tot>fMKU{?_+7QR^ee}cM;K1en0R(9Da)V$a1&H>`c(z0WH@@&3 zm?8YCKabo$r{#FJ#wQ%Mf;q@FuA_%=z%`A3RX)Y=3#RCA54NJ98fkuxX?vI2DMc^) zyW_JUH6K)(XvwkBZtcRimsXhX7S^xt?IoJ2yF%0wzsvPE$8b}CpY5}-QK3Uk`w8>O z(ZbvNND)bkM&|3&w@d;!wmj|vy4#~8mmcj?v{$yTiEVUiLy~usVvP*XiJ&apXR!UuwXOiEjOzhP{9H zTjOs_tRwg;C6}>8*?ElJY0~tQYBV3#aeB;eC_W045y@VKArv=-4yZJ(xxdoE4lEj< zG)1KL3moY1dqI;#Tr0pVzHFofWRy47Epa+QT8@^|pWq%X5v$Gb`JQu%`B+X9AQK}V zrm@E?)1t|ct}9Q!e#s-Zy9UYU*tl**g7~yj3(x-TX?`DUQGB%d1q_5=oCVeW3}DPX z1CM1lGRm%Cahjbny&!rvw3MVx_{Y>}fI7mkr?B!H`kc~4Hi_}Kw?!$Wv_s$--?))1 z4b%-+IuUjssm{@Q9@aQ7((pmlGf7pNO*^U%wn-ncWVCrc`n+7Z-%>QCa2tH=5zE*W z4@Id_mn?1*RRQnLA3Q(&#j2ErM?G3YGQu&N|Oal;t5_$&frlexaXP_c|u3mV2UXHK>pke*+%XneJ5 zXT`$(CSIDm#cHJb`FQ(^j9U>?vPYa0s}k+$PogmyJ#BWyxUk<5<>S<8z%Oy4Ik|D$ z`R0y!q2pGK(~tRmrjRNe9!eqbSb`d$@CZ-01XzDa+fJCz={Fm*#wm)EDlZR0|BqFAH;ifRrN&_EWp!tlJ;ne~7w3jH#cZfN>ytS|dgk+q%-EjqeVb)q(M(@N=e z)ab=0Kicuge&;*dqrOi^=c!baBgMjV>X^z95o4hnUfE0wP72{mT?WSP{ee4aB|mva zRf>i<{3jCqDnKJ%u;X@L*ofGIy;{w!bpkWeBPz zqQ|uV#9lcF{z&^pB}G`zoJ180a4V`!6&YHGMpYEPTusbXyLK;(8m#W0yCxdD84*fI z#deREWUq^|=R#c_!i^qurJ-K2N!a&IWYFB1%l29S9lonr3;10n!jQMtp&Qa!pj>wO z_m9V(k@mmHH`4Lp{HKGLYF$p#IiM$X@Ww#JO&e*|`nQrsm?pwTK z4M%)V%?f6kolgj~&X1VzUh9Cmf2IPBd{JJ0DSwF3?NFN~NvkaTemyLgBRSmtWg|F+S^Fpm!B9r>;>F z=rsMqZYaa#6ROxJR_U+jN`7Q1l~?mTaV@@C?-T|NIB`_m#?*QkAW(x($``kU36Z0@ z96_*oj-wK+C?ma6RCCu#AsewKD)nec@&my>rMT_zKRMZdmyrlU{x#$o)ct$S>p7U? zq$75#+tZ;vbk6as$?|6fHxV14s?`${h%k1+pEZUZFHbK*yg)tJ!gEBuXGH}#7mE=s z7CG8sJC_Y~xZ4M6(>Ey7)LYv*XG2JYu)BE`3k=S9dD55$WF#Y%uG^3MCU6)kxGbML zKPwP?2OuQTu$$dVzjR^vCs8K zVLA_UzU2Gvb9EoK`LU~M*5)s(aY&#T{?OfE#EcofzubE6wWS6UU?`q&_ggw}bFf^a z=&| z=UZb9d&!==2qdiYYY$YdMrm(;OfNd=FLi`X%XoM|1gcqyp7&fn-3Ca>`q3+TCGdUR z*Jh3X7k8Gb^L0`Be&zCJvh2Yn+3O;ID{Ajg1GL37YxpM7Su|H=3vkPEpuKK`^Mfh8 zv~ZFN+v9BrGOa=Cti@~DVR^*PdTXUMp|)~%h0Wt2Hn*X%%LVZVX*=Kkk#3W01pM<% zYjvDx7W#z*7!2zDTXzCVqOHFujS%Bsm_B;Klx@Zv^l~Do&U!>A-eDgrOR$?Yoz$3+1)MDZQY%@J14sP?TT+E6^~zZEBN-b z-E=5GAoy7dYyB~K);lu4gY!<3*CL~{Y)zJ4VF|l?)hQ>uGu_iv2u{>&%}v-O5qtWQ z5pfd$wzH={Vk!Pz);CrPj@4Fm`z3V>9o+J}eYkRUqw@nM(ngYZ$j>3Q#7P0`ncyA7^_nu-+jlNvGp>x(xXI2P3*79at z59f)c`A9GyioW{+uAz~c6hGAM8aqCRIh-wzm%*l_oY&xiy(4lWlc+fpIJ=2_GcG^9 z-MMnwW}0QaVr!at`~AFVQaVEIZ5^Dr({QrP-zE791t*X0;eY>d?(*fuf;!U$!B@hU zi&ChK0#CZAybYAqk7ikKX07ZUd6*;6w!|sZY`%H^;19tH4N!#RVKu{ zwew$K{5&J?RLJg;nRKzF?+#9i!TI8oRLHDIpn*RQA9FWKpMSYF(q2cZ&*@Q#A0fM2 zx{Bc;urZ0n-Pfs$gg-Fx;$uW!g)Rrz#at0z*_;+S-UMXn>$J&A1;vEb&6OD4s;DG7kBq(1E21! zImQXqo@2o@^R}Vb;S!c3on6%QO~3f%$+&tD<%f*b3K}eEy!+HUEObhANQZ_y^AM<> zFUPH}Jqm%fr=YN#a5W)3)eN9-S2mba)A!IO5m;FJSxAAe@OAL=fyu&`Pu0J4Y1^g6 z#5A^t@A}Bnkiz!exZ0~{rgzizz>VmIoh_z*56L9gzzFGd6JRrWy1AHwv->T~zEFeU zN6Mlcy+)XT>_(n2DBB6Ux%<@t%VaCHeWb@>_fkqhnZ_0QT@Of;{AZ6sa(c>_>JsBz ztEhq2L?Kxd{bx^IgZ_a%bg1oKRMerp?8bOkW#GV2y%paq5`9>c)N$9+Oz(D?wg5&4s~Jszp-j^bYfY zbxg{VDj)k20#(G>?Fy>W@K$TVIiCc+CN6b3w2v2Ia8vkGA8`Rpr+S<>mU;v!f6Vz9 znC7%`rO~}NMqaHlCK4f(Et!R)a?bZc^3Cw^tIu(r4b2R$Q_RqKKcTYA#Mn1?zD4kO z7RseoC-9jpMH|(7@hY4D7F}cD`9+_*hNaDk)M3wcE!2#uNI^Uc)`` z9sxn|iZtf_1+^1$ClP%FF`9OLn0bf$gV1~GP)y85jcolQ8Q_Ib=NJ(9heE-_`4rjF z0rb?L&1Sc2(vsh!l=)UOq%e`876as?7BI*^7T2TkDuM)M>iAfB`9ar`VQ5n6dq-N zFlzoqmo$AsWVCC@x=g*@c0==3>wR{<5fk&0c=UTHKg0Ky?Jhp^_d>}w2E@xlP6wTb zWD%U6jwD$EKh&5JHY=T3gZ zz8x|s*v9W)4Yk%thuHCL>&g2wZ@yQ2DwQife@Qm-3*?C%5%Mj~Pju_tIK4OHm^MaO zq=J@#tg+;FTX<6tX*A4D2bx+5#t40Q2}rmI;-ZM3_+5~tCQLFKAS7T!^Jl z%^%_vE=O4R7C-s~u3?ZahR*l)WY5-Nw(@X2yCe6-aVpmu5G}gAEj1%~D6}jkuGb<- z#nP=FW?r)W59?Bn!Qor>wK=w>h5~HUE|; zYmIx*sY6vh?Aqakfq}d4&u(`%F1yFw-^#YMs;6E*bgrB{=0D_0e-qH3M11Xl-%$#c zDF@d9;&`4Ezk3DiSl1J7mAzb?N&vDh7Goh=@p4+BoXj=WAz;Uyx{2vja2=^sWUj`} zB;Tu9T*_7YTDgiOyV#)vAzT#8$XFU>AF)NzA<=?7d&)xnObeq!xAV{D=was5@#_;2 zE-%lS%3e#t;&drYzgnp1oEegBqV8|-P3}nv?%)bNO`K^sQqb4bVAH&?;pO{gfl1eX zFXK=IAAcYqIR5Gxc3j(bWq2K{y*kh;=8nMTphXkdNW`;4jf_zo+?qa7IS$!T#H-ON zn)7?Gr3wKVD+jNd@tGS6v(z_a)h*}E2(5of)OAdT9pEFMD=sh|P~j5;50hAzxO3IW z39RuB#~X9&cjAo1)5MlPi)qTPiI^LI+mv|gwxK!f;P{^9Fj;K#CC;s`b0Di2WITAS zN}qY}NyIpMM4?&gLL3-1qhq|y^p z{gvW)VMe$xu?E_(R-KpR)*UjXUHua^aH@|0$+ljq!dj1T_O>c`qfg&HmyPDhQ{8!j ziJx6yZ?=ac^F*I6!Q(sw-p3w&zU$|Ez|ybZUOX~KyZipg?Nu_Hwy`|aFpy;x>)K?L zjp?Nd!Fn&`oEp*Ukj*KIDOOi7BKYT5h`+F&Mztww{Js%UulaXJ8m&Z;W9{C+Lhm6N z#t5Xw$Q6$<+Hy~YWCJ<#9`uiR!mx?GnU4bL=Zge+f*J?to#ErJ9R}gqt@`-;pIWF^zzXXg1^R6dKuLN z{*jOR>}VhuAOTZSADeF|^wr$w6mp%xHPYP5 zeJiJylx|+m*SnaTSIK;4U}f?1;XeKXywFU#qd{Wd{6T_x&wJ{gXv@+Ts{m+67-HtcP&6_ zw;qTS@Fla=+U)GuAA?3W+pU&40r!jB4&S9iS*A$eDc>;PA#gsVz<&JjAI_LzsvEU& z!`E@jtUmdh1sOKN&UBvTxE*djxG$mcBUm!ktKK_588{&H5SpqENKnu1K=0@=@QK|bQw!K4M1ck8W%*;k=j zO8ldTt);G?%uDvuq`x6CuT!BvR<$zh&14xwikB%=@j(PqXV`arC^z_x?f!ud%Om9c ze~R(yjPZ>9*S!o?E|j|Nw45iWCar4npyO($j@7Z$CqXO~uJfk|dFWpvbhxuEe=V7st4O z2W0>&qR0!r?JzD;t;g2-ya@3=)4Kunf<9k}?F9BZWo?l$38_8+)>lLorJ^5jPg$K0 zt$VsoCE|G{NQadj>Ydz zw}r(>GaxG?spi|lg(Z`OP74L3n9=s3qJ<$K#~@Mrm@@vLt$B4CEZh~b+{i?Nlp!Hd z1MeN1e0FZSSkC9}qGA_6{aArPZQjNrqNVSK?A8@bYP;u-w6r}reqtcki_k_>3GW7s4QGxu}h#-OIYD!}Ba!+4`z9>H(M z~PGy7qh$3G1JgdcX3fT>J zSnAc%3F|wv*|rmbN&0)wDb_atU)R@s@&I@fS`SN(J!I<>t7pNn2%@yAZeI=N=t%rd zV!KHytW{>uUhZ*ZCOnKg@G7{@y;(JvQl}C3ssE6eoJzCD)^huuI3KQ?zst2w5)TVhr#^B(lX`6QtI6WO`Mmg*#8)qkE70@<4=> zqcUJV=R3gO{qGg^OHo}eZC#Lrx&mXlYYvHFL`ylCJ@KexN8ZZYt}-#}_R9y_p`6M` zeN`G23xrQxL3f~8t;JQiedpKqmou}Ai0F9QNx9v_D(x3+Ddk=+P)~v|4N^&!`qQh? zR0Zj4<1;u<1v3ctk6tX{HKG5f?P#A&%h05MP-1Vg9G&n5^S$OdQ^`PFV=W6aLTB`+R@YbLcL8M_%J@pILj zyfv!{Pk84li{Ew~I_Yc~LuiVMK=$t?|K*=&5t^EUR{BJa*@4)r%4W#m{$VcMDmw!B z3TMkJLy+fv#4eV}-=$C7LKS7WSN_PTeMk0hg2r6+eK$z@dc2lbqlc7~auokm}- z#~}!K7rFRUVhy2JEb4#CnJ7=Fvg$J( zO-D%`5@uI3loHl<03`+u0`9{b9S?E0{z0rTo;?1EM?t#j^@er?(k<1-jht5HHry)+ z+|tk`__Y$tV+e25{QlA}wTKnb*{NxM!PshjWahix`GA2Pk9@}A>%?Y9ua((+<%&3e z&98A(+m8S#eq8>%rqiYU!LzcW+%g9{g@i8jV7u@A1H6ZOlZ27kEi8jJ3srJLP2OT= zdZcE;Q%1$st!9fZJ`OxJe72&$<88dq`T1W z+P)n*oUDpNG*hkPsr{V3#7H>Z6w44yXhsDHr)`wdl@Ye*gVR=OBy>Shq(X6P3|k7v zQr)yPjR|(af>2?#9WvzEL2UkQ-Iy(Ry`#2z3@Z)}f=)c@hjYEuz=0Da-g zX;~p+J6Z8PNP%`y>4r3)GgH1M<@erFJRd{RuwehE z<<-&acXp}n>uzP%N`0jm!@gOF=cv|c*ht@Jk48S#Uoy}!a~rSMd=~t<9V2DpLwLe0 z**jxk@zr`JDM^4!!!=T*t(W@_D47pD>RfsnV(;zLD&yj)U47^X#4-Lcg#e}WIeIbM z7@Fqn=5!h)PhIrR`J@i?hrmWgo$u$P2bgGJ?L&37Vf~r0Gt570OE*D~r;#&7K+@d3 z-%7=b1&6ReZL%-z<2Wd;p0Jx_BUYy2^r)!B8~T&?6Yns?xyU6ii6ntKYg~l&6-_#N zFgNq=eVUR@KkKt7v%HeaH|@@Eo#8o;=f5n#*z(oMx_q|yTT2RkaI|r;Kl%T$_`EL& z)zg}Qr!>JD6*)I2IS$7t#nKGOVmV`toX?)<7vvkLL>ukD%b+Kh8zg&-vT>&kC+c3q z5X*aFfkwL36_#o4IclfXNj1JFp50~!;MwRAf4`i>*YkuYVN}JZ4er84tq!u36uqeb zUedjedz4-{v{( zHCZ0O)ZL1oD8}|$9>G-?@-4&^D5!h*H@gvulv*)72u>Edp*7*ai3j(glKXMEf9&}o z$7*MqM#x6e4>M2(LPYrGM0F=c_;sV(1*TjEz4@Yd39dha_~b7Zk{Toyk@nzQ9%lc4 zhukp+%Fb^P4Ut1by^cGEz4CI9`7Q@GaUB@&Dp+TtW-`3bfF5vr1XZomc%zGb7Qs-* zQP}w5A{ zh}5jFiqnz(-TtVo3xB586d~uPSj`D&6Fa`ooIhQVUV^@ou49$9qTp8MU^~!G&(j3K zc~xW({T93EYUJpJd)_@?WgZGzadNfV6#3V11Kl6-HY@&u0Bel0E<6`BS!MepdVEs= zp=hmS=$3ha>+0Jkc8hs?TqK$sNWkL3J+#bByc`a+p|SQ6D4Vm70FuVxssR-83W%@g zI)KqTFOiP1*er}iS|sCdyi0PTW>Ga=gq2RXs%w0Xr21t)mD)QdGNp^-Rls~HANDYJ z*7wSnrmuTHd1a`#lm*g?;9D%*gdJUs9FLpLOT)J8T-b7f4Yo=A_VMP~iW_HavZh*N zIYRqKwzv)pTL3h75%GfV`q##?SWf>mpoY9GGyH>asY+ZE0HjfT2~I=(IeJ(|1S?x{ z82a4Ct%Q<01ZEZMm10*`AI%ytWX#k3$J7GM7OPAv>tQ7iQBmdOjoGp^qV;CB7^aFG z5Uv`cpQ4Yoe60)K>EJBY7I|8Ikz##VZHqBfy8FyP5Gg|BYzR~lVUddYc1GXlRdr%o zaLO6ckeatIFZyKD1ybqJE8I@;tJg~E_O>ri9a&_Z=QyT(9=c;P`7S-pXN&XLUx4X* z&IIL8{LZLxva|N>m4dGV&C+dm5fFaR-`1bJ;-W(cVCKD}C*6pP^&zLmfy9wHPg^TS z$%l5m;LcWHJGB`lF&SoN&I;f!;ifvz=2pDgbW60m(_w_D&pZ%R=6+xE(VK?q&%Po9 z4V*7OdAUUM_Q4mXuGGxtAW|5pf1sN$6Y~K-#3Lw8Ik;%I7n1+T1bRB~pz><X>4k4%X&IZ} zszOmpeY#04l36Bt)bh~`E5XK-%*d06mg`};FDWT^KAS;6lg`var(Lo3r6_frTYE~5 zHnuUZ!ds{GjW=V@CFN7Nat&BulII7b29&NDZ&W~w_`Tj zU$$Y~BE?&tmNg{_qbuHSyvg^Us*y*nt+P)$CTGME>mhFyUXWr&U2eB}x;}sJCEO>U zqz`xjidy}e>6SXI>p}NkQ?yYe!byF#A@V08!nPmDriUqJLvKKjx(YpC|ALOHRs4H< zrQ%0}P>;<4Dtr7~no+%kqF=J&U1wujgJWUu5Qnv$c`aaGYD)mFcB*f_{`fHrDj?~a z{qlp0Et53Gp@9jlMW~oMe-66%mnLE~yt9X6puGJ6u#Knxk;J*x2G*Rpm$t4=H8btZ z^rX&R*=A2F_OaNBk{iWNxb%-M+B1E*lk9%hwQchy?Gzmr@_v>Ut&zDJwxN-??ibEM z%b6zY8y>bLCA^bYX3_?3R>3dYho(ZB@V7^**D!UMLSc%}#w`pkJudGSdOl7!X2S_g z|I~wqG1HbKqEq>ArtuJ7axsmQofc7KbV$5c9kF^oPz>6=s`l}#NwM&Y=&b2$0J>h9S None: os.system("cls") self.show_dev_info() - info_log(f"\n- accounts: {len(config.accounts)}\n- referral_code: {config.referral_code}\n- threads: {config.threads}\n") + info_log( + f"\n- accounts: {len(config.accounts)}\n- referral_code: {config.referral_code}\n- threads: {config.threads}\n" + ) module = self.get_module() if module == "Exit": diff --git a/core/api.py b/core/api.py new file mode 100644 index 0000000..08c4fad --- /dev/null +++ b/core/api.py @@ -0,0 +1,574 @@ +import asyncio +import random + +import httpx +import pyuseragents +import names + +from typing import Literal, List +from noble_tls import Session, Client +from Jam_Twitter_API.account_async import TwitterAccountAsync + +from models import * +from loader import config as configuration + +from .wallet import Wallet +from .modules import * +from .exceptions.base import APIError + + +class MintChainAPI(Wallet): + API_URL = "https://www.mintchain.io/api" + + def __init__(self, account_data: Account): + super().__init__( + mnemonic=account_data.pk_or_mnemonic, rpc_url=configuration.mint_rpc_url + ) + self.account = account_data + self.session = self.setup_session() + self.twitter_account: TwitterAccountAsync = None # type: ignore + + @property + def jwt_token(self) -> str: + return self.session.headers["authorization"].replace("Bearer ", "") + + @property + async def energy_balance(self) -> int: + return (await self.user_info()).energy + + @property + async def tree_size(self) -> int: + return (await self.user_info()).tree + + @property + async def rank(self) -> int: + return (await self.rank_info()).rank + + def setup_session(self) -> Session: + session = Session(client=Client.CHROME_120) + session.random_tls_extension_order = True + + session.timeout_seconds = 15 + session.headers = { + "accept": "application/json, text/plain, */*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8", + "referer": "https://www.mintchain.io", + "user-agent": pyuseragents.random(), + } + session.proxies = { + "http": self.account.proxy, + "https": self.account.proxy, + } + return session + + async def send_request( + self, + request_type: Literal["POST", "GET"] = "POST", + method: str = None, + json_data: dict = None, + params: dict = None, + url: str = None, + headers: dict = None, + verify: bool = True, + ): + def _verify_response(_response: dict) -> dict: + if "code" in _response: + if _response["code"] not in (10000, 200): + raise APIError( + f"{_response.get('msg')} | Method: {method} | URL: {url}" + ) + + return _response + + if request_type == "POST": + if not url: + response = await self.session.post( + f"{self.API_URL}{method}", + json=json_data, + params=params, + headers=headers, + ) + + else: + response = await self.session.post( + url, json=json_data, params=params, headers=headers + ) + + else: + if not url: + response = await self.session.get( + f"{self.API_URL}{method}", params=params, headers=headers + ) + + else: + response = await self.session.get(url, params=params, headers=headers) + + response.raise_for_status() + if verify: + return _verify_response(response.json()) + else: + return response.json() + + async def is_daily_reward_claimed(self) -> bool: + response = await self.send_request( + request_type="GET", method="/tree/energy-list" + ) + return response["result"][-1]["freeze"] + + async def green_id(self) -> dict: + response = await self.send_request(request_type="GET", method="/tree/green-id") + return response + + async def get_energy_list(self, user_id: str = None) -> EnergyListData: + if not user_id: + response = await self.send_request( + request_type="GET", method="/tree/energy-list" + ) + else: + response = await self.send_request( + request_type="GET", + method="/tree/steal/energy-list", + params={"id": user_id}, + ) + + if ( + response + and response.get("msg", "") + == "You are too late, the energy has already been collected by its owner." + ): + return EnergyListData(result=[]) + + return EnergyListData(**response) + + async def get_task_list(self) -> TaskListData: + response = await self.send_request( + request_type="GET", method=f"/tree/task-list?address={self.keypair.address}" + ) + return TaskListData(**response) + + async def complete_tasks(self): + task_list = await self.get_task_list() + for task in task_list.result: + if task.spec not in ("discord-follow", "stake"): + if task.claimed: + logger.debug( + f"Account: {self.account.auth_token} | Task already completed: {task.name}" + ) + continue + + try: + if task.spec in ("twitter-post", "twitter-follow"): + if not self.twitter_account: + self.load_twitter_account() + + if task.spec == "twitter-follow": + user_id = self.twitter_account.get_user_id( + "Mint_Blockchain" + ) + self.twitter_account.follow(user_id) + await self.submit_task_id(task.id) + + else: + tweet_text = "I'm collecting @Mint_Blockchain's ME $MINT in the #MintForest🌳!\n\nMint is the L2 for NFT industry, powered by @nftscan_com and @Optimism.\n\nJoin Mint Forest here: https://mintchain.io/mint-forest\n\n#MintBlockchain #L2forNFT" + + data = self.twitter_account.tweet(tweet_text) + tweet_url = f'https://x.com/JammerCrypto/status/{data["data"]["create_tweet"]["tweet_results"]["result"]["rest_id"]}' + await self.submit_task_id(task.id, twitter_post=tweet_url) + + else: + await self.submit_task_id(task.id) + + logger.debug( + f"Account: {self.account.auth_token} | Task completed: {task.name} | Reward: {task.amount} energy" + ) + await asyncio.sleep(3) + + except APIError as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to complete task: {task.name} | {error}" + ) + await asyncio.sleep(3) + + async def claim_daily_rewards(self) -> None: + energy_list = await self.get_energy_list() + for energy in energy_list.result: + json_data = { + "uid": energy.uid, + "amount": energy.amount, + "includes": energy.includes, + "type": energy.type, + "id": energy.id, + } + + if energy.type == "daily": + if energy.freeze: + logger.debug( + f"Account: {self.account.auth_token} | Daily reward already claimed" + ) + continue + else: + json_data["freeze"] = energy.freeze + + await self.send_request(method="/tree/claim", json_data=json_data) + logger.debug( + f"Account: {self.account.auth_token} | Claimed {energy.amount} energy | Type: {energy.type}" + ) + await asyncio.sleep(1) + + await self.claim_boxes() + + async def bind_invite_code(self) -> ResponseData: + jwt_token = self.jwt_token + + session = Session(client=Client.CHROME_120) + session.headers = { + "accept": "application/json, text/plain, */*", + "accept-language": "sk-SK,sk;q=0.9,en-US;q=0.8,en;q=0.7", + "authorization": "Bearer", + "referer": "https://www.mintchain.io/mint-forest", + "user-agent": self.session.headers["user-agent"], + } + + json_data = { + "code": str(configuration.referral_code), + "jwtToken": jwt_token, + } + + response = await session.get( + "https://www.mintchain.io/api/tree/invitation", params=json_data + ) + return ResponseData(**response.json()) + + async def load_twitter_account(self) -> None: + self.twitter_account = await TwitterAccountAsync.run( + auth_token=self.account.auth_token, + setup_session=True, + proxy=self.account.proxy, + ) + + async def connect_twitter(self) -> dict: + params = { + "code_challenge": "mintchain", + "code_challenge_method": "plain", + "client_id": "enpfUjhndkdrdHhld29aTW96eGM6MTpjaQ", + "redirect_uri": "https://www.mintchain.io/mint-forest", + "response_type": "code", + "scope": "tweet.read users.read follows.read offline.access", + "state": "mintchain", + } + + if not self.twitter_account: + await self.load_twitter_account() + + approved_code = await self.twitter_account.bind_account_v2(params) + + params = { + "code": approved_code, + "jwtToken": self.jwt_token, + "address": self.keypair.address, + } + response = await self.send_request( + url="https://www.mintchain.io/api/twitter/verify", params=params + ) + return response + + async def rank_info(self) -> RankData: + response = await self.send_request(request_type="GET", method="/tree/me-rank") + return RankData(**response["result"]) + + async def user_info(self, tree_id: str = None) -> UserInfo: + if not tree_id: + response = await self.send_request( + request_type="GET", method="/tree/user-info" + ) + else: + response = await self.send_request( + request_type="GET", method="/tree/user-info", params={"treeid": tree_id} + ) + + return UserInfo(**response["result"]) + + async def assets(self) -> List[AssetData]: + response = await self.send_request(request_type="GET", method="/tree/asset") + return [AssetData(**data) for data in response["result"]] + + async def open_box(self, box_id: int) -> OpenBoxData: + json_data = { + "boxId": box_id, + } + + response = await self.send_request(method="/tree/open-box", json_data=json_data) + return OpenBoxData(**response["result"]) + + async def claim_boxes(self): + assets = await self.assets() + for asset in assets: + if not asset.createdAt: + try: + opened_box_data = await self.open_box(asset.id) + logger.debug( + f"Account: {self.account.auth_token} | Box opened | Reward: {opened_box_data.energy} energy" + ) + except APIError as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to open box: {asset.type} | {error}" + ) + + await asyncio.sleep(1) + + async def spin_turntable(self) -> TurntableData: + response = await self.send_request( + request_type="GET", method="/tree/turntable/open" + ) + return TurntableData(**response["result"]) + + async def steal_claim(self, user_id: str) -> None: + json_data = { + "id": user_id, + } + + await self.send_request( + request_type="GET", method="/tree/steal/claim", params=json_data + ) + + async def inject(self, amount: int = None) -> InjectData: + if not amount: + amount = await self.energy_balance + + if amount <= 0: + return InjectData(code=0, result=False, msg="Energy balance is 0") + + json_data = { + "address": self.keypair.address, + "energy": amount, + } + + response = await self.send_request(method="/tree/inject", json_data=json_data) + return InjectData(**response) + + async def fix_sign(self) -> None: + await self.send_request(request_type="GET", method="/tree/fix-sign") + + async def submit_task_id(self, task_id: int, twitter_post: str = None) -> None: + json_data = { + "id": task_id, + } + + if twitter_post: + json_data["twitterurl"] = twitter_post + + await self.send_request(method="/tree/task-submit", json_data=json_data) + + async def get_make_nft_great_again_proofs(self) -> list[str]: + params = { + "user": self.keypair.address, + } + + headers = { + "accept": "application/json, text/plain, */*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8", + "priority": "u=1, i", + "referer": "https://mn-ga.com/?allow=true", + "user-agent": self.session.headers["user-agent"], + } + + async with httpx.AsyncClient(headers=headers) as client: + response = await client.get( + "https://mn-ga.com/api/reward/nft-proof", params=params + ) + response = response.json() + return response["msg"]["proof"] + + async def mint_commemorative_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_commemorative_nft_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint commemorative NFT: {error}") + + async def mint_flag_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_mint_flag_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Flag NFT: {error}") + + async def mint_shop_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_mint_shop_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Shop NFT: {error}") + + async def mint_air3_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_mint_air3_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Air3 NFT: {error}") + + async def mint_green_id_nft(self, tree_id: int) -> tuple[bool | Any, str]: + try: + transaction = await self.build_green_id_nft_transaction(tree_id) + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Green ID NFT: {error}") + + async def mint_vip3_nft(self) -> tuple[bool | Any, str]: + try: + client = Vip3API(self.account) + await client.login() + mint_data = await client.get_mint_data() + transaction = await self.build_vip3_nft_transaction(mint_data) + + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint VIP3 NFT: {error}") + + async def mint_supermint_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_mint_supermint_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint SuperMint NFT: {error}") + + async def mint_summer_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_summer_nft_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Summer NFT: {error}") + + async def mint_make_nft_great_again(self) -> tuple[bool | Any, str]: + try: + proofs = await self.get_make_nft_great_again_proofs() + transaction = await self.build_make_nft_great_again_transaction( + proofs=proofs + ) + + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Make NFT Great Again: {error}") + + async def mint_createx_collection(self) -> tuple[bool | Any, str]: + try: + name = f"{names.get_first_name()} {names.get_last_name()}" + symbol = random.choice( + [f"{name[:3].upper()}", f"{name[:4].upper()}", f"{name[:5].upper()}"] + ) + description = f"Collection of {name} NFTs" + price = random.uniform(0.00001, 0.05) + royalty_fee = random.randint(1, 50) + + client = CreateXAPI( + mnemonic=self.account.pk_or_mnemonic, rpc_url=configuration.mint_rpc_url + ) + await client.login() + collection_id = await client.create_collection( + name=name, + symbol=symbol, + description=description, + price=str(price), + royalty_fee=str(royalty_fee), + ) + await client.create_query_collection(collection_id=collection_id) + trx_data = await client.deploy(collection_id=collection_id) + + transaction = await self.build_createx_collection_transaction(trx_data) + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint OmniHub collection: {error}") + + async def mint_owlto_summer_fest_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_owlto_summer_fest_nft_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint Owlto Summer Fest NFT: {error}") + + async def mint_omnihub_summer_fest_nft(self) -> tuple[bool | Any, str]: + try: + transaction = await self.build_omnihub_summer_fest_nft_transaction() + status, tx_hash = await self.send_and_verify_transaction(transaction) + return status, tx_hash + + except Exception as error: + raise Exception(f"Failed to mint OmniHub Summer Fest NFT: {error}") + + async def verify_wallet(self) -> ResponseData: + json_data = { + "jwtToken": self.jwt_token, + } + + response = await self.send_request(method="/wallet/verify", json_data=json_data) + return ResponseData(**response) + + async def join_airdrop(self) -> dict: + messages = self.sign_mint_message("airdrop") + + params = { + "wallet_address": self.keypair.address, + "signature": messages.signed_message, + "message": messages.message, + "invite_code": "", + } + + return await self.send_request( + request_type="GET", + url="https://mpapi.mintchain.io/api/user/sign", + params=params, + ) + + async def login(self): + messages = self.sign_mint_message("forest") + + json_data = { + "address": self.keypair.address, + "signature": messages.signed_message, + "message": messages.message, + } + + response = await self.send_request(method="/tree/login", json_data=json_data) + data = LoginWalletData(**response["result"]) + self.session.headers["authorization"] = f"Bearer {data.access_token}" + + if data.user.status == "pending": + await self.verify_wallet() + + if not data.user.twitter: + await self.connect_twitter() + logger.debug( + f"Account: {self.account.auth_token} | Twitter account connected" + ) + + if not data.user.inviteId: + await self.bind_invite_code() + logger.debug(f"Account: {self.account.auth_token} | Referral code bound") + + await self.green_id() + await self.assets() + await self.rank_info() + await self.user_info() + await self.get_energy_list() diff --git a/core/bot.py b/core/bot.py new file mode 100644 index 0000000..0c4c9d3 --- /dev/null +++ b/core/bot.py @@ -0,0 +1,549 @@ +import asyncio +import random +from typing import Any + +from models import Account +from loguru import logger +from loader import config + +from .api import MintChainAPI +from .exceptions.base import APIError +from .modules import CometBridge + + +class Bot(MintChainAPI): + def __init__(self, account: Account): + super().__init__(account_data=account) + + async def safe_operation( + self, + operation: callable, + success_message: str, + error_message: str, + retries: int = 0, + delay: int = 3, + argument: Any = None, + ) -> bool: + for _ in range(retries): + try: + await operation() if argument is None else await operation(argument) + logger.success( + f"Account: {self.account.auth_token} | {success_message}" + ) + return True + + except APIError as error: + logger.error( + f"Account: {self.account.auth_token} | {error_message}: {error}" + ) + return False + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | {error_message}: {error} | {'Retrying..' if retries > 0 else ''}" + ) + await asyncio.sleep(delay) + continue + + return False + + async def process_login(self) -> bool: + logger.info(f"Account: {self.account.auth_token} | Logging in..") + return await self.safe_operation( + operation=self.login, + success_message="Logged in", + error_message="Failed to login", + retries=3, + ) + + async def process_fix_sign(self) -> bool: + return await self.safe_operation( + operation=self.fix_sign, + success_message="Fixed sign", + error_message="Failed to fix sign", + retries=2, + ) + + async def process_claim_daily_reward(self) -> bool: + return await self.safe_operation( + operation=self.claim_daily_rewards, + success_message="Finished claiming daily rewards", + error_message="Failed to claim daily rewards", + delay=10, + retries=3, + ) + + async def process_inject(self) -> bool: + return await self.safe_operation( + operation=self.inject, + success_message="Finished injecting energy", + error_message="Failed to inject energy", + retries=3, + ) + + async def process_spin_turntable(self) -> bool: + if config.spin_turntable_by_percentage_of_energy > 0: + try: + balance = await self.energy_balance + if balance < 300: + logger.warning( + f"Account: {self.account.auth_token} | Not enough energy to spin turntable" + ) + return True + + amount = int( + balance * (config.spin_turntable_by_percentage_of_energy / 100) + ) + number_of_spins = int(amount // 300) + if number_of_spins > 5: + number_of_spins = 5 + + for _ in range(number_of_spins): + reward = await self.spin_turntable() + logger.success( + f"Account: {self.account.auth_token} | Opened turntable | Reward: {reward.energy} energy" + ) + await asyncio.sleep(3) + + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to spin turntable: {error}" + ) + + return True + + async def process_show_user_info(self) -> None: + try: + info = await self.tree_size + logger.success( + f"Account: {self.account.auth_token} | Total injected energy: {info} | Daily actions done.." + ) + + except Exception as error: + logger.warning( + f"Account: {self.account.auth_token} | Failed to get user info: {error} | Daily actions done.." + ) + + async def process_mint_comm_nft(self) -> None: + try: + await self.check_balance() + + try: + await self.join_airdrop() + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to join airdrop: {error}" + ) + + logger.info( + f"Account: {self.account.auth_token} | Minting commemorative NFT.." + ) + status, transaction_hash = await self.mint_commemorative_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted commemorative NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint commemorative NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_make_nft_great_again(self) -> None: + try: + await self.check_balance() + logger.info(f"Account: {self.account.auth_token} | Minting MNGA NFT..") + status, transaction_hash = await self.mint_make_nft_great_again() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted MNGA NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint MNGA NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_flag_nft(self) -> None: + try: + await self.check_balance() + logger.info(f"Account: {self.account.auth_token} | Minting Flag NFT..") + status, transaction_hash = await self.mint_flag_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted Flag NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint Flag NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_supermint_nft(self) -> None: + try: + await self.check_balance() + logger.info(f"Account: {self.account.auth_token} | Minting SuperMint NFT..") + status, transaction_hash = await self.mint_supermint_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted SuperMint NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint SuperMint NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_air3_nft(self) -> None: + try: + await self.check_balance() + logger.info(f"Account: {self.account.auth_token} | Minting Air3 NFT..") + status, transaction_hash = await self.mint_air3_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted Air3 NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint Air3 NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_shop_nft(self) -> None: + try: + await self.check_balance() + logger.info(f"Account: {self.account.auth_token} | Minting Shop NFT..") + status, transaction_hash = await self.mint_shop_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted Shop NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint Shop NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_join_airdrop(self) -> None: + try: + await self.join_airdrop() + + except APIError as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to join airdrop: {error}" + ) + return + + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to join airdrop: {error}" + ) + + async def process_mint_vip3_nft(self) -> None: + try: + await self.check_balance() + + try: + await self.join_airdrop() + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to join airdrop: {error}" + ) + + logger.info(f"Account: {self.account.auth_token} | Minting VIP3 NFT..") + status, transaction_hash = await self.mint_vip3_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted VIP3 NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint VIP3 NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_green_id(self) -> None: + try: + await self.check_balance() + logger.info(f"Account: {self.account.auth_token} | Minting Green ID..") + + tree_id = await self.process_get_tree_id() + if not tree_id: + return + + status, transaction_hash = await self.mint_green_id_nft(int(tree_id)) + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted Green ID | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint Green ID | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + # async def process_mint_omnihub_collection(self) -> None: + # try: + # if await self.human_balance() < 0.0001: + # raise Exception("Insufficient balance to mint OmniHub collection | Required: 0.0001 ETH") + # + # logger.info(f"Account: {self.account.auth_token} | Minting OmniHub collection") + # status, transaction_hash = await self.mint_omnihub_collection() + # + # if status: + # logger.success( + # f"Account: {self.account.auth_token} | Minted OmniHub collection | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + # ) + # else: + # logger.error( + # f"Account: {self.account.auth_token} | Failed to mint OmniHub collection | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + # ) + # + # except Exception as error: + # logger.error( + # f"Account: {self.account.auth_token} | {error}" + # ) + + async def process_mint_summer_nft(self) -> None: + try: + if await self.human_balance() < 0.0001: + raise Exception( + "Insufficient balance to mint Summer NFT | Required: 0.0001 ETH" + ) + + logger.info(f"Account: {self.account.auth_token} | Minting Summer NFT..") + status, transaction_hash = await self.mint_summer_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted Summer NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint Summer NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_owlto_summer_fest_nft(self) -> None: + try: + if await self.human_balance() < 0.0001: + raise Exception( + "Insufficient balance to mint Owlto Summer Fest NFT | Required: 0.0001 ETH" + ) + + logger.info( + f"Account: {self.account.auth_token} | Minting Owlto Summer Fest NFT.." + ) + status, transaction_hash = await self.mint_owlto_summer_fest_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted Owlto Summer Fest NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint Owlto Summer Fest NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_mint_omnihub_summer_nft(self) -> None: + try: + if await self.human_balance() < 0.0001: + raise Exception( + "Insufficient balance to mint OmniHub Summer NFT | Required: 0.0001 ETH" + ) + + logger.info( + f"Account: {self.account.auth_token} | Minting OmniHub Summer NFT.." + ) + status, transaction_hash = await self.mint_omnihub_summer_fest_nft() + + if status: + logger.success( + f"Account: {self.account.auth_token} | Minted OmniHub Summer NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to mint OmniHub Summer NFT | Transaction: https://explorer.mintchain.io/tx/{transaction_hash}" + ) + + except Exception as error: + logger.error(f"Account: {self.account.auth_token} | {error}") + + async def process_comet_bridge(self) -> None: + try: + amount_to_bridge = random.uniform( + config.comet_bridge_amount_min, config.comet_bridge_amount_max + ) + client = CometBridge( + amount_to_bridge=amount_to_bridge, + to_address=self.keypair.address, + mnemonic=config.comet_bridge_wallet, + rpc_url=config.arb_rpc_url, + ) + + logger.info( + f"Account: {self.account.auth_token} | Bridging {amount_to_bridge} ETH to MINT (via Comet)" + ) + transaction = await client.build_bridge_transaction() + status, tx_hash = await client.send_and_verify_transaction(transaction) + + if status: + logger.success( + f"Account: {self.account.auth_token} | Bridged {amount_to_bridge} ETH to MINT | Transaction: https://arbiscan.io/tx/{tx_hash}" + ) + + else: + logger.error( + f"Account: {self.account.auth_token} | Failed to bridge {amount_to_bridge} ETH to MINT | Transaction: https://arbiscan.io/tx/{tx_hash}" + ) + + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Error while bridging: {error}" + ) + + async def process_complete_tasks(self): + return await self.safe_operation( + operation=self.complete_tasks, + success_message="Finished completing tasks", + error_message="Failed to complete tasks", + delay=10, + retries=3, + ) + + async def process_get_tree_id(self) -> bool | str: + for _ in range(2): + try: + if not await self.process_login(): + return False + + user_info = await self.user_info() + return str(user_info.treeId) + + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to get tree id: {error} | Retrying.." + ) + await asyncio.sleep(1) + + logger.error( + f"Account: {self.account.auth_token} | Failed to get tree id after 2 retries | Skipping.." + ) + return False + + async def process_mint_random_all_nfts(self) -> None: + operations_dict = { + "mint_comm_nft": self.process_mint_comm_nft, + "mint_make_nft_great_again": self.process_mint_make_nft_great_again, + "mint_flag": self.process_mint_flag_nft, + "mint_shop": self.process_mint_shop_nft, + "mint_air3": self.process_mint_air3_nft, + "mint_supermint": self.process_mint_supermint_nft, + "mint_summer_nft": self.process_mint_summer_nft, + "mint_owlto_summer_nft": self.process_mint_owlto_summer_fest_nft, + "mint_omnihub_summer_nft": self.process_mint_omnihub_summer_nft, + "mint_vip3_nft": self.process_mint_vip3_nft, + "mint_green_id": self.process_mint_green_id, + } + + mint_modules = config.mint_random_all_nfts + random.shuffle(mint_modules) + + for module in mint_modules: + operation = operations_dict.get(module) + if operation: + try: + await operation() + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Failed to process {module}: {error}" + ) + finally: + delay = random.randint( + config.delay_between_mint_min, config.delay_between_mint_max + ) + logger.debug( + f"Account: {self.account.auth_token} | Sleeping for {delay} seconds.." + ) + await asyncio.sleep(delay) + + async def start(self): + random_delay = random.randint( + config.min_delay_before_start, config.max_delay_before_start + ) + logger.info( + f"Account: {self.account.auth_token} | Work will start in {random_delay} seconds.." + ) + await asyncio.sleep(random_delay) + + operations_dict = { + "rewards": [ + self.process_login, + self.process_claim_daily_reward, + self.process_spin_turntable, + self.process_inject, + self.process_show_user_info, + ], + "only_rewards": [ + self.process_login, + self.process_claim_daily_reward, + self.process_show_user_info, + ], + "fix_sign": [self.process_login, self.process_show_user_info], + "mint_comm_nft": [self.process_mint_comm_nft], + "mint_make_nft_great_again": [self.process_mint_make_nft_great_again], + "mint_summer_nft": [self.process_mint_summer_nft], + "mint_flag": [self.process_mint_flag_nft], + "mint_shop": [self.process_mint_shop_nft], + "mint_air3": [self.process_mint_air3_nft], + "mint_supermint": [self.process_mint_supermint_nft], + "comet_bridge": [self.process_comet_bridge], + "mint_random_all_nfts": [self.process_mint_random_all_nfts], + "mint_owlto_summer_nft": [self.process_mint_owlto_summer_fest_nft], + "mint_omnihub_summer_nft": [self.process_mint_omnihub_summer_nft], + "mint_vip3_nft": [self.process_mint_vip3_nft], + "tasks": [self.process_login, self.process_complete_tasks], + "default": [self.process_login, self.process_complete_tasks], + } + + operations = operations_dict.get(config.module, operations_dict["default"]) + + try: + for operation in operations: + if not await operation(): + break + + except Exception as error: + logger.error( + f"Account: {self.account.auth_token} | Unhandled error: {error}" + ) + finally: + logger.success(f"Account: {self.account.auth_token} | Finished") diff --git a/core/exceptions/base.py b/core/exceptions/base.py new file mode 100644 index 0000000..62304da --- /dev/null +++ b/core/exceptions/base.py @@ -0,0 +1,10 @@ +class APIError(Exception): + """Base class for API exceptions""" + + pass + + +class StealEnergyError(APIError): + """Raised when failed to steal energy""" + + pass diff --git a/core/modules/__init__.py b/core/modules/__init__.py new file mode 100644 index 0000000..a41a251 --- /dev/null +++ b/core/modules/__init__.py @@ -0,0 +1,3 @@ +from .comet_bridge import CometBridge +from .createx_api import CreateXAPI +from .vip3_api import Vip3API diff --git a/core/modules/comet_bridge.py b/core/modules/comet_bridge.py new file mode 100644 index 0000000..3ecf1be --- /dev/null +++ b/core/modules/comet_bridge.py @@ -0,0 +1,75 @@ +import json + +from pydantic import HttpUrl +from web3 import AsyncWeb3 + +from core.wallet import Wallet + + +class CometBridge(Wallet): + def __init__( + self, + amount_to_bridge: float, + to_address: str, + mnemonic: str, + rpc_url: HttpUrl | str, + ): + super().__init__(mnemonic, rpc_url) + self.amount_to_bridge = amount_to_bridge + self.to_address = to_address + + @staticmethod + def pad_to_32(x) -> str: + return x.rjust(64, "0") + + async def build_bridge_transaction(self): + function_signature = "0x78499054" + destination_gas_cost = AsyncWeb3.to_wei(0.0003, "ether") + amount = AsyncWeb3.to_wei(self.amount_to_bridge, "ether") + destination_gas_cost + token_address = "0x0000000000000000000000000000000000000000".lower() + provider_address = "0xb50ac92d6d8748ac42721c25a3e2c84637385a6b".lower() + + metadata = {"targetChain": "185", "targetAddress": self.to_address} + + encoded_metadata = ( + f"data:,{json.dumps(metadata, separators=(',', ':'))}".encode("utf-8").hex() + ) + transaction_data = ( + function_signature + + self.pad_to_32(hex(amount)[2:]) + + self.pad_to_32(token_address[2:]) + + self.pad_to_32(provider_address[2:].lower()) + + self.pad_to_32( + hex(32 * 4)[2:] + ) # Смещение для данных (4 параметра по 32 байта) + + self.pad_to_32( + hex(len(encoded_metadata) // 2)[2:] + ) # Длина metadata в байтах + + encoded_metadata + ) + final_data = f"{transaction_data}0000000000000000" + + gas_limit = await self.eth.estimate_gas( + { + "chainId": 42161, + "from": self.keypair.address, + "to": AsyncWeb3.to_checksum_address( + "0x0fbCf4a62036E96C4F6770B38a9B536Aa14d1846" + ), + "value": amount, + "data": final_data, + } + ) + + return { + "chainId": 42161, + "from": self.keypair.address, + "to": AsyncWeb3.to_checksum_address( + "0x0fbCf4a62036E96C4F6770B38a9B536Aa14d1846" + ), + "value": amount, + "gas": gas_limit, + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "data": final_data, + } diff --git a/core/modules/createx_api.py b/core/modules/createx_api.py new file mode 100644 index 0000000..e5207a0 --- /dev/null +++ b/core/modules/createx_api.py @@ -0,0 +1,239 @@ +import asyncio +import json +import time +from datetime import datetime, timezone +from typing import Literal + +import pyuseragents +from eth_account.messages import encode_typed_data +from noble_tls import Client, Session + +from loader import config +from models import Account +from core.exceptions.base import APIError +from core.wallet import Wallet + + +class CreateXAPI(Wallet): + API_URL = "https://createx.art/api" + + def __init__(self, account_data: Account): + super().__init__(account_data.pk_or_mnemonic, config.mint_rpc_url) + self.account = account_data + self.session = self.setup_session() + + def setup_session(self) -> Session: + session = Session(client=Client.CHROME_120) + session.random_tls_extension_order = True + + session.timeout_seconds = 15 + session.headers = { + "accept": "application/json, text/plain, */*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8", + "priority": "u=1, i", + "referer": "https://createx.art/", + "user-agent": pyuseragents.random(), + } + session.proxies = { + "http": self.account.proxy, + "https": self.account.proxy, + } + return session + + async def send_request( + self, + request_type: Literal["POST", "GET"] = "POST", + method: str = None, + json_data: dict = None, + params: dict = None, + url: str = None, + headers: dict = None, + verify: bool = True, + ): + def _verify_response(_response: dict) -> dict: + if "status" in _response: + if _response["status"] != 0: + raise APIError(f"{_response.get('msg')} | Method: {method}") + + return _response + + raise APIError(f"{_response} | Method: {method}") + + if request_type == "POST": + if not url: + response = await self.session.post( + f"{self.API_URL}{method}", + json=json_data, + params=params, + headers=headers, + ) + + else: + response = await self.session.post( + url, json=json_data, params=params, headers=headers + ) + + else: + if not url: + response = await self.session.get( + f"{self.API_URL}{method}", params=params, headers=headers + ) + + else: + response = await self.session.get(url, params=params, headers=headers) + + response.raise_for_status() + if verify: + return _verify_response(response.json()) + else: + return response.json() + + async def get_timestamp(self) -> tuple[int, str]: + response = await self.send_request( + request_type="GET", + method="/v1/creator/public/timestamp", + ) + + timestamp = response["data"]["timestamp_ms"] + timestamp_seconds = timestamp / 1000 + dt = datetime.fromtimestamp(timestamp_seconds, tz=timezone.utc) + formatted_date = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z" + + return timestamp, formatted_date + + async def get_login_data(self) -> tuple[str, str]: + timestamp, formatted_date = await self.get_timestamp() + + message = { + "types": { + "Message": [ + {"name": "Message", "type": "string"}, + {"name": "URI", "type": "string"}, + {"name": "Version", "type": "string"}, + {"name": "ChainId", "type": "uint256"}, + {"name": "Nonce", "type": "uint256"}, + {"name": "issuedAt", "type": "string"}, + ], + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + ], + }, + "primaryType": "Message", + "domain": {"name": "", "version": "1", "chainId": "185"}, + "message": { + "Message": "Sign in to the CreateX", + "URI": "https://createx.art", + "Version": "1", + "ChainId": "185", + "Nonce": timestamp, + "issuedAt": formatted_date, + }, + } + signed_json = { + "domain": {"name": "", "version": "1", "chainId": 185}, + "message": { + "Message": "Sign in to the CreateX", + "URI": "https://createx.art", + "Version": "1", + "ChainId": 185, + "Nonce": timestamp, + "issuedAt": formatted_date, + }, + "primaryType": "Message", + "types": { + "EIP712Domain": [ + {"name": "name", "type": "string"}, + {"name": "version", "type": "string"}, + {"name": "chainId", "type": "uint256"}, + ], + "Message": [ + {"name": "Message", "type": "string"}, + {"name": "URI", "type": "string"}, + {"name": "Version", "type": "string"}, + {"name": "ChainId", "type": "uint256"}, + {"name": "Nonce", "type": "uint256"}, + {"name": "issuedAt", "type": "string"}, + ], + }, + } + + encoded_message = encode_typed_data(full_message=message) + signed_message = self.keypair.sign_message(encoded_message) + return signed_message.signature.hex(), json.dumps(signed_json) + + async def create_collection( + self, name: str, symbol: str, description: str, price: str, royalty_fee: str + ) -> str: + json_data = { + "chain": "MINTCHAIN", + "chain_name": "", + "is_single_create": True, + "nftUpload": True, + "collection_name": name, + "symbol": symbol, + "description": description, + "is_limit_count": False, + "mint_price": price, + "mint_start_time": int(time.time() * 1000), + "mint_end_time": 0, + "mint_qty_per_user": "1", + "royalty_fee": royalty_fee, + "royalty_fee_recipient": self.keypair.address, + "collection_id": False, + "is_image_update": False, + "media_file_count": 1, + "currency": "eth", + } + + response = await self.send_request( + request_type="POST", + method="/v1/createx/create/collection", + json_data=json_data, + ) + return response["data"]["collection_id"] + + async def deploy(self, collection_id: str) -> str: + json_data = { + "chain": "MINTCHAIN", + "chain_name": "MINTCHAIN", + "collection_id": collection_id, + "is_sbt": 0, + } + + response = await self.send_request( + request_type="POST", + method="/v1/createx/create/direct_deploy", + json_data=json_data, + ) + + return f"0x{response['data']['bin']}" + + async def create_query_collection(self, collection_id: str) -> dict: + json_data = { + "chain": "MINTCHAIN", + "chain_name": "MINTCHAIN", + "collection_id": collection_id, + } + + response = await self.send_request( + request_type="POST", + method="/v1/createx/create/query_collection", + json_data=json_data, + ) + return response["data"] + + async def login(self) -> dict: + signature, signed_json = await self.get_login_data() + + response = await self.send_request( + method="/v1/creator/auth/login_with_type_data", + json_data={ + "msg_signature": signature, + "msg_signer": self.keypair.address, + "signed_json": signed_json, + }, + ) + + return response diff --git a/core/modules/vip3_api.py b/core/modules/vip3_api.py new file mode 100644 index 0000000..1251a23 --- /dev/null +++ b/core/modules/vip3_api.py @@ -0,0 +1,128 @@ +import asyncio +import json +import time +from datetime import datetime, timezone +from typing import Literal, Tuple, Any + +import pyuseragents +from eth_account.messages import encode_typed_data, encode_defunct +from noble_tls import Client, Session +from pydantic import HttpUrl + +from loader import config +from models import Account +from core.exceptions.base import APIError +from core.wallet import Wallet + + +class Vip3API(Wallet): + API_URL = "https://dappapi.vip3.io/api" + + def __init__(self, account_data: Account): + super().__init__(account_data.pk_or_mnemonic, config.mint_rpc_url) + self.account = account_data + self.session = self.setup_session() + + def setup_session(self) -> Session: + session = Session(client=Client.CHROME_120) + session.random_tls_extension_order = True + + session.timeout_seconds = 15 + session.headers = { + "accept": "application/json, text/plain, */*", + "accept-language": "en-US,en;q=0.9,ru;q=0.8", + "content-type": "application/json", + "origin": "https://dapp.vip3.io", + "referer": "https://dapp.vip3.io/", + "user-agent": pyuseragents.random(), + } + session.proxies = { + "http": self.account.proxy, + "https": self.account.proxy, + } + return session + + async def send_request( + self, + request_type: Literal["POST", "GET"] = "POST", + method: str = None, + json_data: dict = None, + params: dict = None, + url: str = None, + headers: dict = None, + verify: bool = True, + ): + def _verify_response(_response: dict) -> dict: + if "code" in _response: + if _response["code"] != 0: + raise APIError(f"{_response.get('msg')} | Method: {method}") + + return _response + + raise APIError(f"{_response} | Method: {method}") + + if request_type == "POST": + if not url: + response = await self.session.post( + f"{self.API_URL}{method}", + json=json_data, + params=params, + headers=headers, + ) + + else: + response = await self.session.post( + url, json=json_data, params=params, headers=headers + ) + + else: + if not url: + response = await self.session.get( + f"{self.API_URL}{method}", params=params, headers=headers + ) + + else: + response = await self.session.get(url, params=params, headers=headers) + + response.raise_for_status() + if verify: + return _verify_response(response.json()) + else: + return response.json() + + async def get_login_signature(self) -> tuple[str, Any]: + message = f'Welcome to VIP3!\n\nClick "Sign" to sign in and accept the VIP3 Terms of Use(https://vip3.gitbook.io/term-of-use/).\n\nThis request will not trigger a blockchain transaction or cost any gas fees.\n\nWallet address:\n{self.keypair.address}\n\nNonce: {int(time.time() * 1000)}' + + encoded_message = encode_defunct(text=message) + signed_message = self.keypair.sign_message(encoded_message) + return message, signed_message.signature.hex() + + async def get_mint_data(self) -> dict: + json_data = { + "lang": "en", + "chainId": 185, + } + + response = await self.send_request( + method="/v1/sbt/mint", + json_data=json_data, + ) + return response["data"] + + async def login(self) -> None: + message, signature = await self.get_login_signature() + + json_data = { + "address": self.keypair.address, + "sign": signature, + "raw": message, + } + + response = await self.send_request( + method="/v1/auth", + json_data=json_data, + ) + + self.session.headers.update( + {"Authorization": f"Bearer {response['data']['token']}"} + ) diff --git a/core/wallet.py b/core/wallet.py new file mode 100644 index 0000000..e756809 --- /dev/null +++ b/core/wallet.py @@ -0,0 +1,349 @@ +import random +from typing import Any, Literal + +from eth_account import Account +from eth_account.messages import encode_defunct +from pydantic import HttpUrl +from web3 import AsyncWeb3 +from web3.contract import AsyncContract +from web3.eth import AsyncEth +from web3.types import Nonce + +from models import ( + LoginData, + CommemorativeNFTData, + OmnihubData, + MakeNFTGreatAgainData, + SummerNFTData, + MintFlagData, + MintShopData, + MintAir3Data, + MintSupermintData, + CometBridgeData, + Vip3MintData, + GreenIDData, +) + +Account.enable_unaudited_hdwallet_features() + + +class Wallet(AsyncWeb3, Account): + def __init__(self, mnemonic: str, rpc_url: HttpUrl | str): + super().__init__( + AsyncWeb3.AsyncHTTPProvider(str(rpc_url)), + modules={"eth": (AsyncEth,)}, + middlewares=[], + ) + self.keypair = ( + self.from_mnemonic(mnemonic) + if len(mnemonic.split()) in (12, 24) + else self.from_key(mnemonic) + ) + + @property + def get_commemorative_nft_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(CommemorativeNFTData.address), + abi=CommemorativeNFTData.abi, + ) + + @property + def get_omnihub_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(OmnihubData.address), + abi=OmnihubData.abi, + ) + + @property + def get_make_nft_great_again_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(MakeNFTGreatAgainData.address), + abi=MakeNFTGreatAgainData.abi, + ) + + @property + def get_summer_nft_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(SummerNFTData.address), + abi=SummerNFTData.abi, + ) + + @property + def get_mint_flag_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(MintFlagData.address), + abi=MintFlagData.abi, + ) + + @property + def get_min_shop_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(MintShopData.address), + abi=MintShopData.abi, + ) + + @property + def get_mint_air3_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(MintAir3Data.address), + abi=MintAir3Data.abi, + ) + + @property + def get_mint_supermint_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(MintSupermintData.address), + abi=MintSupermintData.abi, + ) + + @property + def get_comet_bridge_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(CometBridgeData.address), + abi=CometBridgeData.abi, + ) + + @property + def get_vip3_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(Vip3MintData.address), + abi=Vip3MintData.abi, + ) + + @property + def get_green_contract(self) -> AsyncContract: + return self.eth.contract( + address=AsyncWeb3.to_checksum_address(GreenIDData.address), + abi=GreenIDData.abi, + ) + + async def transactions_count(self) -> Nonce: + return await self.eth.get_transaction_count(self.keypair.address) + + async def check_balance(self) -> None: + balance = await self.eth.get_balance(self.keypair.address) + + if balance <= 0: + raise Exception(f"ETH balance is empty") + + async def human_balance(self) -> float | int: + balance = await self.eth.get_balance(self.keypair.address) + return AsyncWeb3.from_wei(balance, "ether") + + async def build_make_nft_great_again_transaction(self, proofs: list[str]): + contract = self.get_make_nft_great_again_contract + transaction = contract.functions.awardItem(proofs) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_green_id_nft_transaction(self, mint_id: int): + contract = self.get_green_contract + transaction = contract.functions.claim(mint_id) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_mint_air3_transaction(self): + contract = self.get_mint_air3_contract + transaction = contract.functions.mint(1) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_mint_supermint_transaction(self): + contract = self.get_mint_supermint_contract + transaction = contract.functions.mint(1) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_mint_shop_transaction(self): + contract = self.get_min_shop_contract + transaction = contract.functions.mint(1) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_mint_flag_transaction(self): + contract = self.get_mint_flag_contract + transaction = contract.functions.mint(1) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_summer_nft_transaction(self): + contract = self.get_summer_nft_contract + transaction = contract.functions.claim( + self.keypair.address, + 1, + AsyncWeb3.to_checksum_address("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"), + 0, + ( + ["0x0000000000000000000000000000000000000000000000000000000000000000"], + 115792089237316195423570985008687907853269984665640564039457584007913129639935, + 0, + AsyncWeb3.to_checksum_address( + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + ), + ), + b"", + ) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_vip3_nft_transaction(self, mint_data: dict): + contract = self.get_vip3_contract + transaction = contract.functions.mint( + self.keypair.address, + mint_data["data"]["deadline"], + mint_data["data"]["level"], + 0, + mint_data["data"]["signature"], + ) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_commemorative_nft_transaction(self): + contract = self.get_commemorative_nft_contract + transaction = contract.functions.mint(1) + + return await transaction.build_transaction( + { + "gasPrice": await self.eth.gas_price, + "nonce": await self.transactions_count(), + "gas": int( + await transaction.estimate_gas({"from": self.keypair.address}) * 1.2 + ), + } + ) + + async def build_createx_collection_transaction(self, data: str): + transaction = { + "from": self.keypair.address, + "data": data, + "nonce": await self.transactions_count(), + } + + estimated_gas = await self.eth.estimate_gas(transaction) + transaction["gas"] = int(estimated_gas * 1.2) + transaction["gasPrice"] = await self.eth.gas_price + + return transaction + + async def build_owlto_summer_fest_nft_transaction(self): + transaction = { + "from": self.keypair.address, + "to": AsyncWeb3.to_checksum_address( + "0x0000C019d60b628F9Ba553092CdA375191319c5e" + ), + "value": AsyncWeb3.to_wei(0.0001, "ether"), + "data": "0x5e752eb40000000000000000000000000c1308dd0b5886b48cb14da2d6cf766cfc8be6ea0000000000000000000000000000000000000000000000000000000000000001", + "nonce": await self.transactions_count(), + } + + estimated_gas = await self.eth.estimate_gas(transaction) + transaction["gas"] = int(estimated_gas * 1.2) + transaction["gasPrice"] = await self.eth.gas_price + + return transaction + + async def build_omnihub_summer_fest_nft_transaction(self): + transaction = { + "from": self.keypair.address, + "to": AsyncWeb3.to_checksum_address( + "0x0000C019d60b628F9Ba553092CdA375191319c5e" + ), + "value": AsyncWeb3.to_wei(0.0001, "ether"), + "data": "0x5e752eb400000000000000000000000050b42f700a5feba13ee6437c43fac4df33062f2b0000000000000000000000000000000000000000000000000000000000000001", + "nonce": await self.transactions_count(), + } + + estimated_gas = await self.eth.estimate_gas(transaction) + transaction["gas"] = int(estimated_gas * 1.2) + transaction["gasPrice"] = await self.eth.gas_price + + return transaction + + @property + def get_forest_message(self) -> str: + message = f"You are participating in the Mint Forest event: \n {self.keypair.address}\n\nNonce: {str(random.randint(1000000, 9000000))}" + return message + + @property + def get_airdrop_message(self) -> str: + message = f"You are participating in the Mint Airdrop event: \n {self.keypair.address}\n\nNonce: {str(random.randint(1000000, 9000000))}" + return message + + def sign_mint_message(self, type_: Literal["airdrop", "forest"]) -> LoginData: + if type_ == "forest": + message = self.get_forest_message + else: + message = self.get_airdrop_message + encoded_message = encode_defunct(text=message) + signed_message = self.keypair.sign_message(encoded_message) + return LoginData(message=message, signed_message=signed_message.signature.hex()) + + async def send_and_verify_transaction(self, trx: Any) -> tuple[bool | Any, str]: + signed = self.keypair.sign_transaction(trx) + tx_hash = await self.eth.send_raw_transaction(signed.rawTransaction) + receipt = await self.eth.wait_for_transaction_receipt(tx_hash) + return receipt["status"] == 1, tx_hash.hex() diff --git a/loader.py b/loader.py index d528938..d116961 100644 --- a/loader.py +++ b/loader.py @@ -1,7 +1,7 @@ import asyncio from models import Config -from config import load_config +from utils import load_config config: Config = load_config() semaphore = asyncio.Semaphore(config.threads) diff --git a/models/account.py b/models/account.py index 5d81b7d..a9b5ff1 100644 --- a/models/account.py +++ b/models/account.py @@ -7,7 +7,6 @@ class Account(BaseModel): pk_or_mnemonic: str proxy: str = None - @field_validator("proxy", mode="before") def check_proxy(cls, value) -> str | None: if not value: diff --git a/models/api.py b/models/api.py index 96a9d97..fb93a64 100644 --- a/models/api.py +++ b/models/api.py @@ -13,11 +13,11 @@ class RankData(BaseModel): class AssetData(BaseModel): - id: int - uid: int - reward: Any + id: int | None = None + uid: int | None = None + reward: Any | None = None type: str = "energy" - openAt: Any + openAt: Any | None = None createdAt: str | None = None @@ -42,18 +42,19 @@ class UserInfo(BaseModel): id: int treeId: int address: str - ens: Any + ens: Any | None = None energy: int tree: int - inviteId: int + inviteId: int | None = None type: str = "normal" - stake_id: int - nft_id: int - nft_pass: int + stake_id: int | None = None + nft_id: int | None = None + nft_pass: int | None = None signin: int - code: Any + code: Any | None = None createdAt: str - invitePercent: int + invitePercent: int | None = None + stealCount: int | None = None class ResponseData(BaseModel): @@ -75,7 +76,6 @@ class User(BaseModel): user: User - class EnergyListData(BaseModel): class Energy(BaseModel): uid: list[str] @@ -84,6 +84,7 @@ class Energy(BaseModel): type: str id: str = None freeze: bool = None + stealable: bool = None @model_validator(mode="before") @classmethod diff --git a/models/config.py b/models/config.py index 4def9ab..374dd2c 100644 --- a/models/config.py +++ b/models/config.py @@ -7,16 +7,21 @@ class Config(BaseModel): accounts: list[Account] referral_code: str | int - eth_rpc_url: HttpUrl - sepolia_rpc_url: HttpUrl + mint_rpc_url: HttpUrl + arb_rpc_url: HttpUrl threads: PositiveInt min_delay_before_start: PositiveInt max_delay_before_start: PositiveInt - min_amount_to_bridge: PositiveFloat - max_amount_to_bridge: PositiveFloat + comet_bridge_wallet: str + comet_bridge_amount_min: PositiveFloat + comet_bridge_amount_max: PositiveFloat + + mint_random_all_nfts: list[str] + delay_between_mint_min: PositiveInt + delay_between_mint_max: PositiveInt spin_turntable_by_percentage_of_energy: int module: str = "" diff --git a/models/onchain.py b/models/onchain.py index 681960b..0fc7b60 100644 --- a/models/onchain.py +++ b/models/onchain.py @@ -1,8 +1,73 @@ from dataclasses import dataclass - @dataclass class BridgeData: address: str = "0x57Fc396328b665f0f8bD235F0840fCeD43128c6b" abi: list = open("./abi/bridge.json", "r").read() + + +@dataclass +class GreenIDData: + address: str = "0x776Fcec07e65dC03E35a9585f9194b8a9082CDdb" + abi: list = open("./abi/green_id.json", "r").read() + + +@dataclass +class CommemorativeNFTData: + address: str = "0xbc4b1cbbfF3Fe2C61Ad2Fd94b91126d5F7593D40" + abi: list = open("./abi/commemorative_nft.json", "r").read() + + +@dataclass +class OmnihubData: + address: str = "0xD473b08745288eF9b412a339ACCfaCce5Cebdd90" + abi: list = open("./abi/omnihub.json", "r").read() + + +@dataclass +class MakeNFTGreatAgainData: + address: str = "0x1a7464938aa694c5dB38Da52114C4fEdBc4EBF6A" + abi: list = open("./abi/make_nft_great_again.json", "r").read() + + +@dataclass +class SummerNFTData: + address: str = "0x98b322D37d54fac46f4980F4171bE2D0Ba8c54C2" + abi: list = open("./abi/summer_nft.json", "r").read() + + +@dataclass +class MintFlagData: + address: str = "0xa6660ba7F9a45e2707efC8dc574aF1DB4319Ee55" + abi: list = open("./abi/mint_flag.json", "r").read() + + +@dataclass +class MintShopData: + address: str = "0xBEEbbAEe8F085F506ce0eA3591f8FBb9C24Af356" + abi: list = open("./abi/mint_shop.json", "r").read() + + +@dataclass +class MintAir3Data: + address: str = "0x38f56A88a9eCD523086804f43Bdf881B8403107a" + abi: list = open("./abi/mint_air3.json", "r").read() + + +@dataclass +class MintSupermintData: + address: str = "0xDD351CDd289d9Bdf88D20EA3c9E316b99dF31412" + abi: list = open("./abi/mint_supermint.json", "r").read() + + +@dataclass +class CometBridgeData: + address: str = "0x0fbCf4a62036E96C4F6770B38a9B536Aa14d1846" + abi: list = open("./abi/cometa.json", "r").read() + + +@dataclass +class Vip3MintData: + address: str = "0xabe292b291A18699b09608de86888D77aD6BAf23" + abi: list = open("./abi/vip3_nft.json", "r").read() diff --git a/requirements.txt b/requirements.txt index 134e481..c90c1de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,16 +1,14 @@ +httpx~=0.27.0 pyuseragents~=1.0.5 -retrying~=1.3.4 +names~=0.3.0 +noble_tls~=0.0.102 +Jam_Twitter_API~=0.6 loguru~=0.7.2 -web3~=6.15.1 -PyYAML~=6.0.1 pydantic~=2.6.4 +web3~=6.15.1 art~=6.1 -orjson~=3.9.15 -httpx~=0.27.0 -tqdm~=4.66.2 -noble_tls~=0.0.102 -eth_account -curl_cffi~=0.6.2 +PyYAML~=6.0.1 urllib3~=2.2.1 inquirer~=3.2.4 -colorama~=0.4.6 \ No newline at end of file +colorama~=0.4.6 +better_proxy diff --git a/run.py b/run.py index c16cce3..29bca5b 100644 --- a/run.py +++ b/run.py @@ -1,13 +1,16 @@ import asyncio import sys +from typing import Any + import urllib3 -from loguru import logger +from loguru import logger from loader import config, semaphore -from src.bot import Bot +from core.bot import Bot from models import Account from console import Console +from utils import export_trees_ids def setup(): @@ -27,15 +30,55 @@ async def run_safe(account: Account): await Bot(account).start() +async def run_get_tree_info_module(account: Account) -> tuple[Any, bool | str]: + async with semaphore: + client = Bot(account) + tree_id = await client.process_get_tree_id() + if tree_id: + logger.info(f"Account: {account.auth_token} | Tree ID: {tree_id}") + return client.keypair.address, tree_id + + async def run(): while True: Console().build() - tasks = [ - asyncio.create_task(run_safe(account)) for account in config.accounts - ] - await asyncio.gather(*tasks) - input("\nPress Enter to continue...") + if config.module in ( + "bridge", + "rewards", + "tasks", + "fix_sign", + "mint_comm_nft", + "only_rewards", + "mint_omnihub", + "mint_make_nft_great_again", + "mint_summer_nft", + "mint_flag", + "mint_shop", + "mint_air3", + "mint_supermint", + "comet_bridge", + "mint_all_nfts", + "mint_owlto_summer_nft", + "mint_omnihub_summer_nft", + "mint_random_all_nfts", + "mint_vip3_nft", + "mint_green_id", + ): + tasks = [ + asyncio.create_task(run_safe(account)) for account in config.accounts + ] + await asyncio.gather(*tasks) + + elif config.module == "export_trees_ids": + tasks = [ + asyncio.create_task(run_get_tree_info_module(account)) + for account in config.accounts + ] + results = await asyncio.gather(*tasks) + export_trees_ids(results) + + input("\n\nPress Enter to continue...") if __name__ == "__main__": diff --git a/src/api.py b/src/api.py deleted file mode 100644 index 354c987..0000000 --- a/src/api.py +++ /dev/null @@ -1,334 +0,0 @@ -import asyncio -import random -import pyuseragents - -from typing import Literal, List -from noble_tls import Session, Client - -from models import EnergyListData -from twitter_api import Account as TwitterAccount -from twitter_api.models import BindAccountParamsV2 - -from models import * -from loader import config as configuration - -from .wallet import Wallet -from .exceptions.base import APIError -from .bridge import Bridge - - -class MintChainAPI(Wallet): - API_URL = "https://www.mintchain.io/api" - - def __init__(self, account_data: Account): - super().__init__(mnemonic=account_data.pk_or_mnemonic, rpc_url=configuration.eth_rpc_url) - self.account = account_data - self.session = self.setup_session() - self.twitter_account: TwitterAccount = None # type: ignore - - @property - def jwt_token(self) -> str: - return self.session.headers["authorization"].replace("Bearer ", "") - - @property - async def energy_balance(self) -> int: - return (await self.user_info()).energy - - @property - async def tree_size(self) -> int: - return (await self.user_info()).tree - - @property - async def rank(self) -> int: - return (await self.rank_info()).rank - - def setup_session(self) -> Session: - session = Session(client=Client.CHROME_120) - session.random_tls_extension_order = True - - session.timeout_seconds = 15 - session.headers = { - "authority": "www.mintchain.io", - "accept": "application/json, text/plain, */*", - "accept-language": "en-US,en;q=0.9,ru;q=0.8", - "authorization": "Bearer", - "content-type": "application/json", - "origin": "https://www.mintchain.io", - "referer": "https://www.mintchain.io", - "user-agent": pyuseragents.random(), - } - session.proxies = { - "http": self.account.proxy, - "https": self.account.proxy, - } - return session - - async def send_request( - self, - request_type: Literal["POST", "GET"] = "POST", - method: str = None, - json_data: dict = None, - params: dict = None, - url: str = None, - ): - def _verify_response(_response: dict) -> dict: - if "code" in _response: - if _response["code"] != 10000: - raise APIError(f"{_response.get('msg')} | Method: {method}") - - return _response - - raise APIError(f"{_response} | Method: {method}") - - if request_type == "POST": - if not url: - response = await self.session.post( - f"{self.API_URL}{method}", json=json_data, params=params - ) - - else: - response = await self.session.post(url, json=json_data, params=params) - - else: - if not url: - response = await self.session.get( - f"{self.API_URL}{method}", params=params - ) - - else: - response = await self.session.get(url, params=params) - - response.raise_for_status() - return _verify_response(response.json()) - - async def is_daily_reward_claimed(self) -> bool: - response = await self.send_request( - request_type="GET", method="/tree/energy-list" - ) - return response["result"][-1]["freeze"] - - - async def get_energy_list(self) -> EnergyListData: - response = await self.send_request(request_type="GET", method="/tree/energy-list") - return EnergyListData(**response) - - async def get_task_list(self) -> TaskListData: - response = await self.send_request(request_type="GET", method=f"/tree/task-list?address={self.keypair.address}") - return TaskListData(**response) - - async def complete_tasks(self): - task_list = await self.get_task_list() - for task in task_list.result: - if task.spec not in ("discord-follow", "stake"): - if task.claimed: - logger.debug(f"Account: {self.account.auth_token} | Task already completed: {task.name}") - continue - - try: - if task.spec in ("twitter-post", "twitter-follow"): - if not self.twitter_account: - self.load_twitter_account() - - if task.spec == "twitter-follow": - user_id = self.twitter_account.get_user_id("Mint_Blockchain") - self.twitter_account.follow(user_id) - await self.submit_task_id(task.id) - - else: - tweet_text = "I'm collecting @Mint_Blockchain's ME $MINT in the #MintForest🌳!\n\nMint is the L2 for NFT industry, powered by @nftscan_com and @Optimism.\n\nJoin Mint Forest here: https://mintchain.io/mint-forest\n\n#MintBlockchain #L2forNFT" - - data = self.twitter_account.tweet(tweet_text) - tweet_url = f'https://x.com/JammerCrypto/status/{data["data"]["create_tweet"]["tweet_results"]["result"]["rest_id"]}' - await self.submit_task_id(task.id, twitter_post=tweet_url) - - else: - await self.submit_task_id(task.id) - - logger.debug(f"Account: {self.account.auth_token} | Task completed: {task.name} | Reward: {task.amount} energy") - await asyncio.sleep(3) - - except APIError as error: - logger.error(f"Account: {self.account.auth_token} | Failed to complete task: {task.name} | {error}") - await asyncio.sleep(3) - - async def claim_daily_rewards(self) -> None: - energy_list = await self.get_energy_list() - for energy in energy_list.result: - json_data = { - "uid": energy.uid, - "amount": energy.amount, - "includes": energy.includes, - "type": energy.type, - "id": energy.id, - } - - if energy.type == "daily": - if energy.freeze: - logger.debug(f"Account: {self.account.auth_token} | Daily reward already claimed") - continue - else: - json_data["freeze"] = energy.freeze - - await self.send_request(method="/tree/claim", json_data=json_data) - logger.debug(f"Account: {self.account.auth_token} | Claimed {energy.amount} energy | Type: {energy.type}") - await asyncio.sleep(1) - - await self.claim_boxes() - - async def bind_invite_code(self) -> ResponseData: - jwt_token = self.jwt_token - - session = Session(client=Client.CHROME_120) - session.headers = { - "accept": "application/json, text/plain, */*", - "accept-language": "sk-SK,sk;q=0.9,en-US;q=0.8,en;q=0.7", - "authorization": "Bearer", - "referer": "https://www.mintchain.io/mint-forest", - "user-agent": self.session.headers["user-agent"], - } - - json_data = { - "code": str(configuration.referral_code), - "jwtToken": jwt_token, - } - - response = await session.get( - "https://www.mintchain.io/api/tree/invitation", params=json_data - ) - return ResponseData(**response.json()) - - def load_twitter_account(self) -> None: - self.twitter_account = TwitterAccount.run( - auth_token=self.account.auth_token, - setup_session=True, - proxy=self.account.proxy, - ) - - - async def connect_twitter(self) -> dict: - params = { - "code_challenge": "mintchain", - "code_challenge_method": "plain", - "client_id": "enpfUjhndkdrdHhld29aTW96eGM6MTpjaQ", - "redirect_uri": "https://www.mintchain.io/mint-forest", - "response_type": "code", - "scope": "tweet.read users.read follows.read offline.access", - "state": "mintchain", - } - - if not self.twitter_account: - self.load_twitter_account() - - bind_data = self.twitter_account.bind_account_v2( - bind_params=BindAccountParamsV2(**params) - ) - - params = { - "code": bind_data.code, - "jwtToken": self.jwt_token, - "address": self.keypair.address, - } - response = await self.send_request( - url="https://www.mintchain.io/api/twitter/verify", params=params - ) - return response - - async def rank_info(self) -> RankData: - response = await self.send_request(request_type="GET", method="/tree/me-rank") - return RankData(**response["result"]) - - async def user_info(self) -> UserInfo: - response = await self.send_request(request_type="GET", method="/tree/user-info") - return UserInfo(**response["result"]) - - async def assets(self) -> List[AssetData]: - response = await self.send_request(request_type="GET", method="/tree/asset") - return [AssetData(**data) for data in response["result"]] - - async def open_box(self, box_id: int) -> OpenBoxData: - json_data = { - "boxId": box_id, - } - - response = await self.send_request(method="/tree/open-box", json_data=json_data) - return OpenBoxData(**response["result"]) - - async def claim_boxes(self): - assets = await self.assets() - for asset in assets: - if not asset.createdAt: - try: - opened_box_data = await self.open_box(asset.id) - logger.debug(f"Account: {self.account.auth_token} | Box opened | Reward: {opened_box_data.energy} energy") - except APIError as error: - logger.error(f"Account: {self.account.auth_token} | Failed to open box: {asset.type} | {error}") - - await asyncio.sleep(1) - - async def spin_turntable(self) -> TurntableData: - response = await self.send_request(request_type="GET", method="/tree/turntable/open") - return TurntableData(**response["result"]) - - async def inject(self, amount: int = None) -> InjectData: - if not amount: - amount = await self.energy_balance - - if amount <= 0: - return InjectData(code=0, result=False, msg="Energy balance is 0") - - json_data = { - "address": self.keypair.address, - "energy": amount, - } - - response = await self.send_request(method="/tree/inject", json_data=json_data) - return InjectData(**response) - - - async def submit_task_id(self, task_id: int, twitter_post: str = None) -> None: - json_data = { - 'id': task_id, - } - - if twitter_post: - json_data["twitterurl"] = twitter_post - - await self.send_request(method="/tree/task-submit", json_data=json_data) - - async def testnet_bridge(self) -> None: - amount_to_bridge = random.uniform(configuration.min_amount_to_bridge, configuration.max_amount_to_bridge) - bridge = Bridge(amount=amount_to_bridge, mnemonic_or_pk=self.account.pk_or_mnemonic) - bridge.send_transaction() - - async def verify_wallet(self) -> ResponseData: - json_data = { - "jwtToken": self.jwt_token, - } - - response = await self.send_request(method="/wallet/verify", json_data=json_data) - return ResponseData(**response) - - async def login(self): - messages = self.sign_login_message() - json_data = { - "address": self.keypair.address, - "signature": messages.signed_message, - "message": messages.message, - } - - response = await self.send_request(method="/tree/login", json_data=json_data) - data = LoginWalletData(**response["result"]) - self.session.headers["authorization"] = f"Bearer {data.access_token}" - - if data.user.status == "pending": - await self.verify_wallet() - - if not data.user.twitter: - await self.connect_twitter() - logger.debug( - f"Account: {self.account.auth_token} | Twitter account connected" - ) - - if not data.user.inviteId: - await self.bind_invite_code() - logger.debug(f"Account: {self.account.auth_token} | Referral code bound") diff --git a/src/bot.py b/src/bot.py deleted file mode 100644 index 251a73d..0000000 --- a/src/bot.py +++ /dev/null @@ -1,166 +0,0 @@ -import asyncio -import random -from typing import Any - -from models import Account -from loguru import logger -from loader import config - -from .api import MintChainAPI - - -class Bot(MintChainAPI): - def __init__(self, account: Account): - super().__init__(account_data=account) - - async def safe_operation( - self, - operation: callable, - success_message: str, - error_message: str, - retries: int = 0, - delay: int = 3, - argument: Any = None, - ) -> bool: - for _ in range(retries): - try: - await operation() if argument is None else await operation(argument) - logger.success( - f"Account: {self.account.auth_token} | {success_message}" - ) - return True - - except Exception as error: - logger.error( - f"Account: {self.account.auth_token} | {error_message}: {error} | {'Retrying..' if retries > 0 else ''}" - ) - await asyncio.sleep(delay) - continue - - return False - - async def process_login(self) -> bool: - return await self.safe_operation( - operation=self.login, - success_message="Logged in", - error_message="Failed to login", - retries=3, - ) - - async def process_claim_daily_reward(self) -> bool: - return await self.safe_operation( - operation=self.claim_daily_rewards, - success_message="Finished claiming daily rewards", - error_message="Failed to claim daily rewards", - delay=10, - retries=3, - ) - - async def process_inject(self) -> bool: - return await self.safe_operation( - operation=self.inject, - success_message="Finished injecting energy", - error_message="Failed to inject energy", - retries=3, - ) - - async def process_spin_turntable(self) -> bool: - if config.spin_turntable_by_percentage_of_energy > 0: - try: - balance = await self.energy_balance - if balance < 300: - logger.warning( - f"Account: {self.account.auth_token} | Not enough energy to spin turntable" - ) - return True - - amount = int(balance * (config.spin_turntable_by_percentage_of_energy / 100)) - number_of_spins = int(amount // 300) - if number_of_spins > 5: - number_of_spins = 5 - - for _ in range(number_of_spins): - reward = await self.spin_turntable() - logger.success(f"Account: {self.account.auth_token} | Opened turntable | Reward: {reward.energy} energy") - await asyncio.sleep(3) - - except Exception as error: - logger.error( - f"Account: {self.account.auth_token} | Failed to spin turntable: {error}" - ) - - return True - - - async def process_show_user_info(self) -> None: - try: - info = await self.tree_size - logger.success( - f"Account: {self.account.auth_token} | Total injected energy: {info} | Daily actions done.." - ) - - except Exception as error: - logger.warning( - f"Account: {self.account.auth_token} | Failed to get user info: {error} | Daily actions done.." - ) - - async def process_testnet_bridge(self) -> bool: - return await self.safe_operation( - operation=self.testnet_bridge, - success_message="Testnet bridge completed", - error_message="Failed to complete testnet bridge", - delay=30, - retries=3, - ) - - async def process_complete_tasks(self): - return await self.safe_operation( - operation=self.complete_tasks, - success_message="Finished completing tasks", - error_message="Failed to complete tasks", - delay=10, - retries=3, - ) - - async def start(self): - random_delay = random.randint(config.min_delay_before_start, config.max_delay_before_start) - logger.info( - f"Account: {self.account.auth_token} | Work will start in {random_delay} seconds.." - ) - await asyncio.sleep(random_delay) - - try: - if config.module == "rewards": - operations = [ - self.process_login, - self.process_claim_daily_reward, - self.process_spin_turntable, - self.process_inject, - self.process_show_user_info, - ] - - elif config.module == "bridge": - operations = [ - self.process_login, - self.process_testnet_bridge, - ] - - else: - operations = [ - self.process_login, - self.process_complete_tasks, - ] - - for operation in operations: - if not await operation(): - break - - except Exception as error: - logger.error( - f"Account: {self.account.auth_token} | Unhandled error: {error}" - ) - - finally: - logger.success( - f"Account: {self.account.auth_token} | Finished" - ) diff --git a/src/bridge.py b/src/bridge.py deleted file mode 100644 index 7646e9f..0000000 --- a/src/bridge.py +++ /dev/null @@ -1,72 +0,0 @@ -import re - -from web3 import Web3, Account -from web3.exceptions import TransactionIndexingInProgress, TransactionNotFound, TimeExhausted - -from models import BridgeData -from loguru import logger -from loader import config as cfg - - -class Bridge(Web3, Account): - def __init__(self, amount: float, mnemonic_or_pk: str): - super().__init__(Web3.HTTPProvider(cfg.sepolia_rpc_url)) - - self.amount = amount - self.nonce = None - self.keypair = self.from_mnemonic(mnemonic_or_pk) if len(mnemonic_or_pk.split()) in (12, 24) else self.from_key(mnemonic_or_pk) - self.contract = self.eth.contract(address=Web3.to_checksum_address(BridgeData.address), abi=BridgeData.abi) - - @property - def address(self): - return self.keypair.address - - def build_transaction(self): - transaction = self.contract.functions.bridgeETHTo( - self.address, - 200000, - b"0x7375706572627269646765" - ) - - return transaction.build_transaction({ - "value": Web3.to_wei(self.amount, "ether"), - "gasPrice": self.eth.gas_price, - "nonce": self.eth.get_transaction_count(self.address) if not self.nonce else self.nonce, - "gas": int( - transaction.estimate_gas({"from": self.address}) * 1.2 - ), - }) - - def check_balance(self) -> None: - balance = self.eth.get_balance(self.address) - human_balance = self.from_wei(balance, "ether") - - if human_balance < self.amount: - raise Exception(f"Insufficient balance: {human_balance} | Required: {self.amount} ETH") - - - def send_transaction(self) -> bool: - try: - self.check_balance() - - signed_transaction = self.keypair.sign_transaction(self.build_transaction()) - transaction_hash = self.eth.send_raw_transaction(signed_transaction.rawTransaction) - logger.debug(f"Account: {self.address} | Bridging {self.amount} ETH | Transaction hash: {transaction_hash.hex()}") - status = self.eth.wait_for_transaction_receipt(transaction_hash, timeout=60) - - if status.status != 1: - raise Exception(f"Failed to bridge {self.amount} ETH to MINT, transaction failed") - - logger.success(f"Account: {self.address} | Bridged {self.amount} ETH to MINT") - - except (TimeExhausted, TransactionNotFound, TransactionIndexingInProgress) as error: - logger.error(f"Account: {self.address} | Transaction not found or time exhausted | {error} | Retrying...") - return self.send_transaction() - - except Exception as error: - if "nonce too low" in str(error): - self.nonce = int(re.search(r"next nonce (\d+)", str(error)).group(1)) - logger.warning(f"Account: {self.address} | Nonce too low | Next nonce: {self.nonce} | Retrying...") - return self.send_transaction() - - raise Exception(f"Failed to bridge {self.amount} ETH | {error}") diff --git a/src/exceptions/base.py b/src/exceptions/base.py deleted file mode 100644 index a2dcb07..0000000 --- a/src/exceptions/base.py +++ /dev/null @@ -1,4 +0,0 @@ -class APIError(Exception): - """Base class for API exceptions""" - - pass diff --git a/src/wallet.py b/src/wallet.py deleted file mode 100644 index 294a8fa..0000000 --- a/src/wallet.py +++ /dev/null @@ -1,31 +0,0 @@ -from eth_account import Account -from eth_account.messages import encode_defunct -from web3 import Web3 -from web3.types import Nonce - -from models import LoginData - - -Account.enable_unaudited_hdwallet_features() - - -class Wallet(Web3, Account): - def __init__(self, mnemonic: str, rpc_url: str): - super().__init__(Web3.HTTPProvider(rpc_url)) - self.keypair = self.from_mnemonic(mnemonic) if len(mnemonic.split()) in (12, 24) else self.from_key(mnemonic) - - @property - def transactions_count(self) -> Nonce: - return self.eth.get_transaction_count(self.keypair.address) - - @property - def get_message(self) -> str: - message = f"You are participating in the Mint Forest event: \n {self.keypair.address}\n\nNonce: {self.transactions_count}" - return message - - def sign_login_message(self) -> LoginData: - encoded_message = encode_defunct(text=self.get_message) - signed_message = self.keypair.sign_message(encoded_message) - return LoginData( - message=self.get_message, signed_message=signed_message.signature.hex() - ) diff --git a/twitter_api/__init__.py b/twitter_api/__init__.py deleted file mode 100644 index b4831a4..0000000 --- a/twitter_api/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .account import Account -from .errors import * diff --git a/twitter_api/account.py b/twitter_api/account.py deleted file mode 100644 index c806bb9..0000000 --- a/twitter_api/account.py +++ /dev/null @@ -1,1664 +0,0 @@ -import asyncio -import hashlib -import math -import mimetypes -import platform -import secrets -import httpx - -from copy import deepcopy -from datetime import datetime -from string import ascii_letters -from typing import Coroutine -from uuid import uuid1, getnode - -from curl_cffi import requests -from curl_cffi.requests.session import Response -from httpx import Cookies, Headers -from tqdm import tqdm - -from .models import * -from .constants import * -from .errors import TwitterAccountSuspended, RateLimitError -from .models import BindAccountDataV1 -from .util import * - -# logging.getLogger("httpx").setLevel(logging.WARNING) - -if platform.system() != "Windows": - try: - import uvloop - - uvloop.install() - except ImportError as e: - ... - - -class Account: - def __init__(self): - self._session: requests.Session = requests.Session() - self._proxy: str = "" - self._reformatted_proxy: str = "" - - self.gql_api = "https://twitter.com/i/api/graphql" - self.v1_api = "https://api.twitter.com/1.1" - self.v2_api = "https://twitter.com/i/api/2" - - @classmethod - def run( - cls, - auth_token: str = None, - cookies: dict[str] = None, - proxy: str = None, - setup_session: bool = True, - ) -> "Account": - account = cls() - account._proxy = proxy - if proxy: - if proxy.startswith("http://"): - account._session = requests.Session( - proxies={"http://": account.proxy}, timeout=30, verify=False - ) - account._reformatted_proxy = account.proxy - - else: - account._reformatted_proxy = account.get_reformatted_proxy - account._session = requests.Session( - proxies=( - {"http://": account._reformatted_proxy} - if account._reformatted_proxy - else None - ), - timeout=30, - verify=False, - ) - - if not (auth_token, cookies): - raise TwitterError( - { - "error_message": "Failed to authenticate account. You need to set cookies or auth_token." - } - ) - - if setup_session: - if auth_token: - account.session.cookies.update({"auth_token": auth_token}) - account.setup_session() - else: - account.session.cookies.update(cookies) - - else: - if not account.session.cookies.get( - "auth_token" - ) and not account.session.cookies.get("ct0"): - account.session.cookies.update({"auth_token": auth_token}) - account.setup_session() - else: - account.session.cookies.update(cookies) - - return account - - @property - def get_auth_data(self) -> dict: - return { - "auth_token": self.auth_token, - "cookies": dict(self.cookies), - "proxy": self.proxy, - } - - def gql( - self, - method: str, - operation: tuple, - variables: dict, - features: dict = Operation.default_features, - ) -> dict: - qid, op = operation - params = { - "queryId": qid, - "features": features, - "variables": Operation.default_variables | variables, - } - if method == "POST": - data = {"json": params} - else: - data = {"params": {k: orjson.dumps(v).decode() for k, v in params.items()}} - - r = self.session.request( - method=method, - url=f"{self.gql_api}/{qid}/{op}", - headers=self.session.headers, - allow_redirects=True, - **data, - ) - - return self._verify_response(r) - - def v1(self, path: str, params: dict) -> dict: - headers = get_headers(self.session) - headers["content-type"] = "application/x-www-form-urlencoded" - r = self.session.post( - f"{self.v1_api}/{path}", headers=headers, data=params, allow_redirects=True - ) - return self._verify_response(r) - - @staticmethod - def _verify_response(r: Response) -> dict: - try: - rate_limit_remaining = r.headers.get("x-rate-limit-remaining") - if rate_limit_remaining and int(rate_limit_remaining) in (0, 1): - reset_ts = int(r.headers.get("x-rate-limit-reset")) - raise RateLimitError( - f"Rate limit reached. Reset in {reset_ts - int(time.time())} seconds. " - ) - # logger.info( - # f"Rate limit reached | Reset in {reset_ts - int(time.time())} seconds | Sleeping..." - # ) - # current_ts = int(time.time()) - # difference = reset_ts - current_ts - # asyncio.sleep(difference) - - data = r.json() - except ValueError: - raise TwitterError( - { - "error_message": f"Failed to parse response: {r.text}. " - "If you are using proxy, make sure it is not blocked by Twitter." - } - ) - - if "errors" in data: - error_message = ( - data["errors"][0].get("message") if data["errors"] else data["errors"] - ) - - error_code = data["errors"][0].get("code") if data["errors"] else None - - if isinstance(error_message, str) and error_message.lower().startswith( - "to protect our users from spam and other" - ): - raise TwitterAccountSuspended(error_message) - - raise TwitterError( - { - "error_code": error_code, - "error_message": error_message, - } - ) - - try: - r.raise_for_status() - except httpx.HTTPError as http_error: - raise TwitterError( - { - "error_message": str(http_error), - } - ) - - return data - - @property - def proxy(self): - return self._proxy - - @property - def get_reformatted_proxy(self): - try: - if self.proxy is None: - return None - - ip, port, username, password = self.proxy.split(":") - return f"http://{username}:{password}@{ip}:{port}" - - except (ValueError, AttributeError): - raise TwitterError( - { - "error_message": "Failed to parse proxy. " - "Make sure you are using correct proxy format: " - "ip:port:username:password" - } - ) - - @property - def session(self): - return self._session - - @property - def cookies(self) -> Cookies: - return self._session.cookies - - @property - def headers(self) -> Headers: - return self._session.headers - - @property - def auth_token(self) -> str: - return self._session.cookies.get("auth_token", "") - - @property - def ct0(self) -> str: - return self._session.cookies.get("ct0", "") - - def request_ct0(self) -> str: - url = "https://twitter.com/i/api/2/oauth2/authorize" - r = self.session.get(url, allow_redirects=True) - - if "ct0" in r.cookies: - return r.cookies.get("ct0") - else: - raise TwitterError( - { - "error_message": "Failed to get ct0 token. " - "Make sure you are using correct cookies." - } - ) - - def request_guest_token( - self, session: requests.Session, csrf_token: str = None - ) -> str: - if not (csrf_token, self.session.cookies.get("ct0", "")): - raise TwitterError( - { - "error_message": "Failed to get guest token. " - "Make sure you are using correct cookies." - } - ) - - headers = { - "content-type": "application/x-www-form-urlencoded", - "authorization": "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA", - "x-csrf-token": ( - csrf_token if csrf_token else self.session.cookies.get("ct0") - ), - } - r = session.post( - f"{self.v1_api}/guest/activate.json", - headers=headers, - allow_redirects=True, - ) - - data = self._verify_response(r) - return data["guest_token"] - - def setup_session(self): - session = requests.Session( - # follow_redirects=True, - proxies={"http://": self._reformatted_proxy} if self.proxy else None, - timeout=30, - verify=False, - ) - - generated_csrf_token = secrets.token_hex(16) - guest_token = self.request_guest_token(session, generated_csrf_token) - - cookies = {"ct0": generated_csrf_token, "gt": guest_token} - headers = {"x-guest-token": guest_token, "x-csrf-token": generated_csrf_token} - - self.session.headers.update(headers) - self.session.cookies.update(cookies) - csrf_token = self.request_ct0() - - self.session.headers["x-csrf-token"] = csrf_token - self.session.cookies.delete("ct0") - self.session.cookies.update({"ct0": csrf_token}) - self.session.headers = get_headers(self.session) - - self.verify_credentials() - self.session.headers.update( - {"x-csrf-token": self.session.cookies.get("ct0", domain=".twitter.com")} - ) - - def bind_account_v1(self, bind_params: BindAccountParamsV1) -> BindAccountDataV1: - - def get_oauth_token() -> str: - _response = requests.get(str(bind_params.url), allow_redirects=True) - raise_for_status(_response) - - token = re.search( - r' 1: - return token[1] - - raise TwitterError( - { - "error_message": "Failed to get oauth token. " - "Make sure you are using correct cookies or url." - } - ) - - def get_authenticity_token(_oauth_token: str) -> BindAccountDataV1 | str: - params = { - "oauth_token": _oauth_token, - } - _response = self.session.get( - "https://api.twitter.com/oauth/authenticate", params=params - ) - raise_for_status(_response) - - token = re.search( - r' str: - data = { - "authenticity_token": _authenticity_token, - "redirect_after_login": f"https://api.twitter.com/oauth/authorize?oauth_token={_oauth_token}", - "oauth_token": _oauth_token, - } - - response = self.session.post( - "https://api.twitter.com/oauth/authorize", - data=data, - allow_redirects=True, - ) - raise_for_status(response) - - _confirm_url = re.search( - r' BindAccountDataV1: - response = self.session.get(_url, allow_redirects=True) - raise_for_status(response) - - if "status=error" in response.url: - raise TwitterError( - { - "error_message": "Failed to bind account. " - "Make sure you are using correct cookies or url." - } - ) - - _oauth_token, _oauth_verifier = response.url.split("oauth_token=")[1].split( - "&oauth_verifier=" - ) - return BindAccountDataV1( - url=response.url, - oauth_token=_oauth_token, - oauth_verifier=_oauth_verifier, - ) - - oauth_token = get_oauth_token() - authenticity_token = get_authenticity_token(oauth_token) - - if isinstance(authenticity_token, BindAccountDataV1): - return authenticity_token - - confirm_url = get_confirm_url(oauth_token, authenticity_token) - return process_confirm_url(confirm_url) - - def bind_account_v2(self, bind_params: BindAccountParamsV2) -> BindAccountDataV2: - - def get_auth_code() -> str: - response = self.session.get( - "https://twitter.com/i/api/2/oauth2/authorize", - params=bind_params.model_dump(), - ) - raise_for_status(response) - self.session.headers.update( - {"x-csrf-token": self.session.cookies.get("ct0", domain=".twitter.com")} - ) - return response.json()["auth_code"] - - def approve_auth_code(_auth_code: str) -> str: - _params = { - "approval": "true", - "code": _auth_code, - } - - response = self.session.post( - "https://twitter.com/i/api/2/oauth2/authorize", - params=_params, - allow_redirects=True, - ) - raise_for_status(response) - - code = response.json()["redirect_uri"].split("code=")[1] - return code - - auth_code = get_auth_code() - approved_code = approve_auth_code(auth_code) - return BindAccountDataV2(code=approved_code) - - def create_poll(self, text: str, choices: list[str], poll_duration: int) -> dict: - options = { - "twitter:card": "poll4choice_text_only", - "twitter:api:api:endpoint": "1", - "twitter:long:duration_minutes": poll_duration, # max: 10080 - } - for i, c in enumerate(choices): - options[f"twitter:string:choice{i + 1}_label"] = c - - headers = get_headers(self.session) - headers["content-type"] = "application/x-www-form-urlencoded" - url = "https://caps.twitter.com/v2/cards/create.json" - - r = self.session.post( - url, - headers=headers, - params={"card_data": orjson.dumps(options).decode()}, - allow_redirects=True, - ) - card_uri = (self._verify_response(r))["card_uri"] - - data = self.tweet(text, poll_params={"card_uri": card_uri}) - return data - - def verify_credentials(self) -> dict: - r = self.session.get( - f"{self.v1_api}/account/verify_credentials.json", allow_redirects=True - ) - return self._verify_response(r) - - def email_phone_info(self) -> dict: - r = self.session.get( - f"{self.v1_api}/users/email_phone_info.json", allow_redirects=True - ) - return self._verify_response(r) - - def settings_info(self) -> dict: - r = self.session.get( - f"{self.v1_api}/account/settings.json", allow_redirects=True - ) - return self._verify_response(r) - - def screen_name(self) -> str: - data = self.verify_credentials() - return data["screen_name"] - - def user_id(self) -> int: - data = self.verify_credentials() - return data["id"] - - def name(self) -> str: - data = self.verify_credentials() - return data["name"] - - def location(self) -> str: - data = self.verify_credentials() - return data["location"] - - def description(self) -> str: - data = self.verify_credentials() - return data["description"] - - def followers_count(self) -> int: - data = self.verify_credentials() - return data["followers_count"] - - def friends_count(self) -> int: - data = self.verify_credentials() - return data["friends_count"] - - def registration_date(self) -> str: - data = self.verify_credentials() - return data["created_at"] - - def suspended(self) -> bool: - data = self.verify_credentials() - return data["suspended"] - - def dm(self, text: str, receivers: list[int], media: str = "") -> dict: - variables = { - "message": {}, - "requestId": str(uuid1(getnode())), - "target": {"participant_ids": receivers}, - } - if media: - media_id = self.upload_media(media, is_dm=True) - variables["message"]["media"] = {"id": media_id, "text": text} - else: - variables["message"]["text"] = {"text": text} - - res = self.gql("POST", Operation.useSendMessageMutation, variables) - if find_key(res, "dm_validation_failure_type"): - raise TwitterError( - { - "error_message": "Failed to send message. Sender does not have privilege to dm receiver(s)", - "error_code": 349, - } - ) - return res - - def custom_dm(self, text: str, receiver: int) -> dict: - json_data = { - "event": { - "type": "message_create", - "message_create": { - "target": {"recipient_id": f"{receiver}"}, - "message_data": {"text": f"{text}"}, - }, - } - } - - r = self.session.post( - f"{self.v1_api}/direct_messages/events/new.json", - json=json_data, - ) - return self._verify_response(r) - - def delete_tweet(self, tweet_id: int | str) -> dict: - variables = {"tweet_id": tweet_id, "dark_request": False} - return self.gql("POST", Operation.DeleteTweet, variables) - - def tweet( - self, text: str, *, media: List[MediaEntity] = None, **kwargs - ) -> dict: - variables = { - "tweet_text": text, - "dark_request": False, - "media": { - "media_entities": [], - "possibly_sensitive": False, - }, - "semantic_annotation_ids": [], - } - - if reply_params := kwargs.get("reply_params", {}): - variables |= reply_params - if quote_params := kwargs.get("quote_params", {}): - variables |= quote_params - if poll_params := kwargs.get("poll_params", {}): - variables |= poll_params - - draft = kwargs.get("draft") - schedule = kwargs.get("schedule") - - if draft or schedule: - variables = { - "post_tweet_request": { - "auto_populate_reply_metadata": False, - "status": text, - "exclude_reply_user_ids": [], - "media_ids": [], - }, - } - if media: - for m in media: - media_id = self.upload_media(m["media"]) - variables["post_tweet_request"]["media_ids"].append(media_id) - if alt := m.get("alt"): - self._add_alt_text(media_id, alt) - - if schedule: - variables["execute_at"] = ( - datetime.strptime(schedule, "%Y-%m-%d %H:%M").timestamp() - if isinstance(schedule, str) - else schedule - ) - return self.gql("POST", Operation.CreateScheduledTweet, variables) - - return self.gql("POST", Operation.CreateDraftTweet, variables) - - # regular tweet - if media: - for m in media: - - tagged_users_id = [] - for tagged_user in m.tagged_users: - user_id = self.get_user_id(tagged_user) - tagged_users_id.append(user_id) - - variables["media"]["media_entities"].append( - {"media_id": m.media_id, "tagged_users": tagged_users_id} - ) - - return self.gql("POST", Operation.CreateTweet, variables) - - def schedule_tweet( - self, text: str, date: int | str, *, media: List[MediaEntity] = None - ) -> dict: - variables = { - "post_tweet_request": { - "auto_populate_reply_metadata": False, - "status": text, - "exclude_reply_user_ids": [], - "media_ids": [], - }, - "execute_at": ( - datetime.strptime(date, "%Y-%m-%d %H:%M").timestamp() - if isinstance(date, str) - else date - ), - } - if media: - for m in media: - - tagged_users_id = [] - for tagged_user in m.tagged_users: - user_id = self.get_user_id(tagged_user) - tagged_users_id.append(user_id) - - variables["media"]["media_entities"].append( - {"media_id": m.media_id, "tagged_users": tagged_users_id} - ) - - return self.gql("POST", Operation.CreateScheduledTweet, variables) - - def schedule_reply( - self, text: str, date: int | str, tweet_id: int, *, media: list = None - ) -> dict: - variables = { - "post_tweet_request": { - "auto_populate_reply_metadata": True, - "in_reply_to_status_id": tweet_id, - "status": text, - "exclude_reply_user_ids": [], - "media_ids": [], - }, - "execute_at": ( - datetime.strptime(date, "%Y-%m-%d %H:%M").timestamp() - if isinstance(date, str) - else date - ), - } - if media: - for m in media: - media_id = self.upload_media(m["media"]) - variables["post_tweet_request"]["media_ids"].append(media_id) - if alt := m.get("alt"): - self._add_alt_text(media_id, alt) - - return self.gql("POST", Operation.CreateScheduledTweet, variables) - - def unschedule_tweet(self, tweet_id: int | str) -> dict: - variables = {"scheduled_tweet_id": tweet_id} - return self.gql("POST", Operation.DeleteScheduledTweet, variables) - - def untweet(self, tweet_id: int | str) -> dict: - variables = {"tweet_id": tweet_id, "dark_request": False} - return self.gql("POST", Operation.DeleteTweet, variables) - - def reply( - self, text: str, tweet_id: int | str, media: List[MediaEntity] = None - ) -> dict: - variables = { - "tweet_text": text, - "reply": { - "in_reply_to_tweet_id": tweet_id, - "exclude_reply_user_ids": [], - }, - "batch_compose": "BatchSubsequent", - "dark_request": False, - "media": { - "media_entities": [], - "possibly_sensitive": False, - }, - "semantic_annotation_ids": [], - } - - if media: - for m in media: - tagged_users_id = [] - - for tagged_user in m.tagged_users: - user_id = self.get_user_id(tagged_user) - tagged_users_id.append(user_id) - - variables["media"]["media_entities"].append( - {"media_id": m.media_id, "tagged_users": tagged_users_id} - ) - - return self.gql("POST", Operation.CreateTweet, variables) - - def quote(self, text: str, tweet_id: int) -> dict: - variables = { - "tweet_text": text, - # can use `i` as it resolves to screen_name - "attachment_url": f"https://twitter.com/i/status/{tweet_id}", - "dark_request": False, - "media": { - "media_entities": [], - "possibly_sensitive": False, - }, - "semantic_annotation_ids": [], - } - return self.gql("POST", Operation.CreateTweet, variables) - - def retweet(self, tweet_id: int) -> dict: - variables = {"tweet_id": tweet_id, "dark_request": False} - return self.gql("POST", Operation.CreateRetweet, variables) - - def unretweet(self, tweet_id: int) -> dict: - variables = {"source_tweet_id": tweet_id, "dark_request": False} - return self.gql("POST", Operation.DeleteRetweet, variables) - - @staticmethod - def __get_cursor_value(data: dict, target_cursor_type: str, target_entry_type: str): - if target_entry_type != "threaded_conversation_with_injections_v2": - for instruction in ( - data.get("data", {}) - .get(target_entry_type, {}) - .get("timeline", {}) - .get("instructions", []) - ): - for entry in instruction.get("entries", []): - content = entry.get("content", {}) - cursor_type = content.get("cursorType") - if ( - content.get("entryType") == "TimelineTimelineCursor" - and cursor_type == target_cursor_type - ): - return content.get("value") - - else: - for instruction in ( - data.get("data", {}).get(target_entry_type, {}).get("instructions", []) - ): - for entry in instruction.get("entries", []): - content = entry.get("content", {}) - cursor_type = content.get("cursorType") - if ( - content.get("entryType") == "TimelineTimelineCursor" - and cursor_type == target_cursor_type - ): - return content.get("value") - - return None - - def tweet_likes( - self, celery_task, tweet_id: int, limit: int = 0 - ) -> dict[str, list[dict]]: - variables = {"tweetId": tweet_id, "count": 100} - users_data = [] - - while True: - data = self.gql("GET", Operation.Favoriters, variables) - - for instruction in ( - data.get("data", {}) - .get("favoriters_timeline", {}) - .get("timeline", {}) - .get("instructions", []) - ): - try: - for entry in instruction["entries"]: - try: - result = entry["content"]["itemContent"]["user_results"][ - "result" - ] - screen_name = result["legacy"]["screen_name"] - if screen_name not in ( - user["screen_name"] for user in users_data - ): - users_data.append( - self.get_user_data_from_user_results(result) - ) - - except (KeyError, TypeError, IndexError): - continue - - except KeyError: - return {"users": users_data[:limit] if limit > 0 else users_data} - - cursor_value = self.__get_cursor_value( - data, "Bottom", "favoriters_timeline" - ) - if not cursor_value or (0 < limit <= len(users_data)): - return {"users": users_data[:limit] if limit > 0 else users_data} - - variables["cursor"] = cursor_value - - def tweet_retweeters( - self, celery_task, tweet_id: int, limit: int = 0 - ) -> dict[str, list[Any]]: - variables = {"tweetId": tweet_id, "count": 100} - tweets_data = [] - - while True: - data = self.gql("GET", Operation.Retweeters, variables) - - for instruction in data["data"]["retweeters_timeline"]["timeline"][ - "instructions" - ]: - try: - for entry in instruction["entries"]: - try: - result = entry["content"]["itemContent"]["user_results"][ - "result" - ] - screen_name = result["legacy"]["screen_name"] - if screen_name not in ( - user["screen_name"] for user in tweets_data - ): - tweets_data.append( - self.get_user_data_from_user_results(result) - ) - except (KeyError, TypeError, IndexError): - continue - - except KeyError: - return {"users": tweets_data[:limit] if limit > 0 else tweets_data} - - cursor_value = self.__get_cursor_value( - data, "Bottom", "retweeters_timeline" - ) - - if not cursor_value or (0 < limit <= len(tweets_data)): - return {"users": tweets_data[:limit] if limit > 0 else tweets_data} - - variables["cursor"] = cursor_value - - @staticmethod - def get_user_data_from_user_results(data: dict) -> dict: - legacy = data.get("legacy", {}) - - return { - "id": data.get("rest_id"), - "name": legacy.get("name"), - "screen_name": legacy.get("screen_name"), - "profile_image_url": legacy.get("profile_image_url_https"), - "favourites_count": legacy.get("favourites_count"), - "followers_count": legacy.get("followers_count"), - "friends_count": legacy.get("friends_count"), - "location": legacy.get("location"), - "description": legacy.get("description"), - "created_at": legacy.get("created_at"), - } - - def tweet_replies( - self, celery_task, tweet_id: int, limit: int = 0 - ) -> dict[str, list[dict[str, dict | Any]]]: - variables = {"focalTweetId": tweet_id} - replies_data = [] - - while True: - data = self.gql("GET", Operation.TweetDetail, variables) - - for entry in data["data"]["threaded_conversation_with_injections_v2"][ - "instructions" - ][0]["entries"]: - try: - result = entry["content"]["items"][0]["item"]["itemContent"][ - "tweet_results" - ]["result"] - reply_text = result["legacy"]["full_text"] - user_results = result["core"]["user_results"]["result"] - - if reply_text not in ( - reply["reply_text"] for reply in replies_data - ): - replies_data.append( - { - "reply_text": reply_text, - "user_data": self.get_user_data_from_user_results( - user_results - ), - } - ) - except (KeyError, TypeError, IndexError): - continue - - entries = data["data"]["threaded_conversation_with_injections_v2"][ - "instructions" - ][0]["entries"] - if not entries[-1]["entryId"].startswith("cursor-bottom") or ( - 0 < limit <= len(replies_data) - ): - return {"replies": replies_data[:limit] if limit > 0 else replies_data} - - for entry in entries: - if entry["entryId"].startswith("cursor-bottom"): - cursor_value = entry["content"]["itemContent"]["value"] - variables["cursor"] = cursor_value - break - - def user_followers(self, celery_task, username: str, limit: int = 0) -> list[str]: - variables = {"screen_name": username, "count": 200} - users = [] - - while True: - r = self.session.get(f"{self.v1_api}/followers/list.json", params=variables) - if r.status_code == 503: - asyncio.sleep(3) - continue - - else: - data = self._verify_response(r) - new_users = [user["screen_name"] for user in data["users"]] - users.extend(new_users) - - next_cursor = int(data.get("next_cursor")) - if next_cursor == 0 or (0 < limit <= len(users)): - return users[:limit] if limit > 0 else users - - variables["cursor"] = data["next_cursor_str"] - - def user_followings(self, username: str) -> list[str]: - variables = {"screen_name": username, "count": 200} - users = [] - - while True: - r = self.session.get(f"{self.v1_api}/friends/list.json", params=variables) - if r.status_code == 503: - asyncio.sleep(5) - continue - - else: - data = self._verify_response(r) - new_users = [user["screen_name"] for user in data["users"]] - users.extend(new_users) - - if int(data.get("next_cursor")) == 0: - return users - - variables["cursor"] = data["next_cursor_str"] - - def user_last_tweets( - self, user_id: int, username: str - ) -> list[dict[str, str, str | None, str | None, str | None]]: - data = self.gql("GET", Operation.UserTweets, {"userId": user_id}) - - try: - tweets_data = [] - timeline = data["data"]["user"]["result"]["timeline_v2"]["timeline"] - for tweet in timeline["instructions"]: - entries = tweet.get("entries", []) - for entry in entries: - if entry["entryId"].startswith("tweet"): - tweet_link = f"https://twitter.com/{username}/status/{entry['entryId'].split('-')[-1]}" - else: - continue - - tweet_results = ( - entry.get("content", {}) - .get("itemContent", {}) - .get("tweet_results", {}) - .get("result", {}) - .get("legacy") - ) - if tweet_results and tweet_results.get("full_text"): - full_text = tweet_results["full_text"] - created_at = tweet_results.get("created_at", "") - is_quote_status = tweet_results.get("is_quote_status", "") - lang = tweet_results.get("lang", "") - - tweets_data.append( - { - "tweet_link": tweet_link, - "full_text": full_text, - "created_at": created_at, - "is_quote_status": is_quote_status, - "lang": lang, - } - ) - - return tweets_data - - except Exception as error: - raise TwitterError({"error_message": f"Failed to get user tweets: {error}"}) - - def like(self, tweet_id: int) -> dict: - variables = {"tweet_id": tweet_id} - return self.gql("POST", Operation.FavoriteTweet, variables) - - def unlike(self, tweet_id: int) -> dict: - variables = {"tweet_id": tweet_id} - return self.gql("POST", Operation.UnfavoriteTweet, variables) - - def bookmark(self, tweet_id: int) -> dict: - variables = {"tweet_id": tweet_id} - return self.gql("POST", Operation.CreateBookmark, variables) - - def unbookmark(self, tweet_id: int) -> dict: - variables = {"tweet_id": tweet_id} - return self.gql("POST", Operation.DeleteBookmark, variables) - - def create_list(self, name: str, description: str, private: bool) -> dict: - variables = { - "isPrivate": private, - "name": name, - "description": description, - } - return self.gql("POST", Operation.CreateList, variables) - - def update_list( - self, list_id: int, name: str, description: str, private: bool - ) -> dict: - variables = { - "listId": list_id, - "isPrivate": private, - "name": name, - "description": description, - } - return self.gql("POST", Operation.UpdateList, variables) - - def update_pinned_lists(self, list_ids: list[int]) -> dict: - """ - Update pinned lists. - Reset all pinned lists and pin all specified lists in the order they are provided. - - @param list_ids: list of list ids to pin - @return: response - """ - return self.gql("POST", Operation.ListsPinMany, {"listIds": list_ids}) - - def pin_list(self, list_id: int) -> dict: - return self.gql("POST", Operation.ListPinOne, {"listId": list_id}) - - def unpin_list(self, list_id: int) -> dict: - return self.gql("POST", Operation.ListUnpinOne, {"listId": list_id}) - - def add_list_member(self, list_id: int, user_id: int) -> dict: - return self.gql( - "POST", Operation.ListAddMember, {"listId": list_id, "userId": user_id} - ) - - def remove_list_member(self, list_id: int, user_id: int) -> dict: - return self.gql( - "POST", Operation.ListRemoveMember, {"listId": list_id, "userId": user_id} - ) - - def delete_list(self, list_id: int) -> dict: - return self.gql("POST", Operation.DeleteList, {"listId": list_id}) - - def update_list_banner(self, list_id: int, media: str) -> dict: - media_id = self.upload_media(media) - variables = {"listId": list_id, "mediaId": media_id} - return self.gql("POST", Operation.EditListBanner, variables) - - def delete_list_banner(self, list_id: int) -> dict: - return self.gql("POST", Operation.DeleteListBanner, {"listId": list_id}) - - def follow_topic(self, topic_id: int) -> dict: - return self.gql("POST", Operation.TopicFollow, {"topicId": str(topic_id)}) - - def unfollow_topic(self, topic_id: int) -> dict: - return self.gql("POST", Operation.TopicUnfollow, {"topicId": str(topic_id)}) - - def pin(self, tweet_id: int) -> dict: - return self.v1( - "account/pin_tweet.json", {"tweet_mode": "extended", "id": tweet_id} - ) - - def unpin(self, tweet_id: int) -> dict: - return self.v1( - "account/unpin_tweet.json", {"tweet_mode": "extended", "id": tweet_id} - ) - - def get_user_id(self, username: str) -> int: - headers = get_headers(self.session) - headers["content-type"] = "application/x-www-form-urlencoded" - r = self.session.get( - f"{self.v1_api}/users/show.json", - headers=headers, - params={"screen_name": username}, - ) - data = self._verify_response(r) - return data["id"] - - def get_user_info(self, username: str) -> dict: - headers = get_headers(self.session) - headers["content-type"] = "application/x-www-form-urlencoded" - r = self.session.get( - f"{self.v1_api}/users/show.json", - headers=headers, - params={"screen_name": username}, - ) - return self._verify_response(r) - - def follow(self, user_id: int | str) -> dict: - settings = deepcopy(follow_settings) - settings |= {"user_id": user_id} - return self.v1("friendships/create.json", settings) - - def unfollow(self, user_id: int | str) -> dict: - settings = deepcopy(follow_settings) - settings |= {"user_id": user_id} - return self.v1("friendships/destroy.json", settings) - - def mute(self, user_id: int) -> dict: - return self.v1("mutes/users/create.json", {"user_id": user_id}) - - def unmute(self, user_id: int) -> dict: - return self.v1("mutes/users/destroy.json", {"user_id": user_id}) - - def enable_follower_notifications(self, user_id: int) -> dict: - settings = deepcopy(follower_notification_settings) - settings |= {"id": user_id, "device": "true"} - return self.v1("friendships/update.json", settings) - - def disable_follower_notifications(self, user_id: int) -> dict: - settings = deepcopy(follower_notification_settings) - settings |= {"id": user_id, "device": "false"} - return self.v1("friendships/update.json", settings) - - def block(self, user_id: int) -> dict: - return self.v1("blocks/create.json", {"user_id": user_id}) - - def unblock(self, user_id: int) -> dict: - return self.v1("blocks/destroy.json", {"user_id": user_id}) - - def update_profile_image(self, media: str) -> dict: - media_id = self.upload_media(media) - params = {"media_id": media_id} - - r = self.session.post( - f"{self.v1_api}/account/update_profile_image.json", - headers=get_headers(self.session), - params=params, - ) - return self._verify_response(r) - - def update_profile_banner(self, media: str) -> dict: - media_id = self.upload_media(media) - params = {"media_id": media_id} - - r = self.session.post( - f"{self.v1_api}/account/update_profile_banner.json", - headers=get_headers(self.session), - params=params, - ) - return self._verify_response(r) - - def update_profile_info(self, params: dict) -> dict: - headers = get_headers(self.session) - r = self.session.post( - f"{self.v1_api}/account/update_profile.json", headers=headers, params=params - ) - - return self._verify_response(r) - - def update_search_settings(self, settings: dict) -> dict: - twid = int(self.session.cookies.get("twid").split("=")[-1].strip('"')) - headers = get_headers(self.session) - - r = self.session.post( - url=f"{self.v1_api}/strato/column/User/{twid}/search/searchSafety", - headers=headers, - json=settings, - ) - return self._verify_response(r) - - def update_settings(self, settings: dict) -> dict: - return self.v1("account/settings.json", settings) - - def update_username(self, username: str): - return self.update_settings({"screen_name": username}) - - def change_password(self, old: str, new: str) -> dict: - params = { - "current_password": old, - "password": new, - "password_confirmation": new, - } - headers = get_headers(self.session) - headers["content-type"] = "application/x-www-form-urlencoded" - - r = self.session.post( - f"{self.v1_api}/account/change_password.json", - headers=headers, - data=params, - allow_redirects=True, - ) - return self._verify_response(r) - - def remove_interests(self, *args) -> dict: - """ - Pass 'all' to remove all interests - """ - r = self.session.get( - f"{self.v1_api}/account/personalization/twitter_interests.json", - headers=get_headers(self.session), - ) - current_interests = r.json()["interested_in"] - if args == "all": - disabled_interests = [x["id"] for x in current_interests] - else: - disabled_interests = [ - x["id"] for x in current_interests if x["display_name"] in args - ] - payload = { - "preferences": { - "interest_preferences": { - "disabled_interests": disabled_interests, - "disabled_partner_interests": [], - } - } - } - r = self.session.post( - f"{self.v1_api}/account/personalization/p13n_preferences.json", - headers=get_headers(self.session), - json=payload, - ) - return self._verify_response(r) - - def home_timeline(self, limit=math.inf) -> list[dict]: - return self._paginate( - "POST", Operation.HomeTimeline, Operation.default_variables, int(limit) - ) - - def home_latest_timeline(self, limit=math.inf) -> list[dict]: - return self._paginate( - "POST", - Operation.HomeLatestTimeline, - Operation.default_variables, - int(limit), - ) - - def bookmarks(self, limit=math.inf) -> list[dict]: - return self._paginate("GET", Operation.Bookmarks, {}, int(limit)) - - def _paginate( - self, method: str, operation: tuple, variables: dict, limit: int - ) -> list[dict]: - initial_data = self.gql(method, operation, variables) - res = [initial_data] - ids = set(find_key(initial_data, "rest_id")) - dups = 0 - DUP_LIMIT = 3 - - cursor = get_cursor(initial_data) - while (dups < DUP_LIMIT) and cursor: - prev_len = len(ids) - if prev_len >= limit: - return res - - variables["cursor"] = cursor - data = self.gql(method, operation, variables) - - cursor = get_cursor(data) - ids |= set(find_key(data, "rest_id")) - - if prev_len == len(ids): - dups += 1 - - res.append(data) - return res - - def custom_upload_media(self, file: Path) -> int | None: - url = "https://upload.twitter.com/1.1/media/upload.json" - - headers = get_headers(self.session) - with httpx.Client( - headers=headers, cookies=dict(self.session.cookies) - ) as client: - upload_type = "tweet" - media_type = mimetypes.guess_type(file)[0] - media_category = ( - f"{upload_type}_gif" - if "gif" in media_type - else f'{upload_type}_{media_type.split("/")[0]}' - ) - - files = {"media": file.read_bytes()} - - post_data = {} - if media_category is not None: - post_data["media_category"] = media_category - - r = client.post(url=url, json=params, params=post_data, files=files) - - data = self._verify_response(r) - return data["media_id"] - - def upload_media(self, filename: str, is_dm: bool = False) -> int | None: - """ - https://developer.twitter.com/en/docs/twitter-api/v1/media/upload-media/uploading-media/media-best-practices - """ - - def check_media(category: str, size: int) -> None: - fmt = lambda x: f"{(x / 1e6):.2f} MB" - msg = ( - lambda x: f"cannot upload {fmt(size)} {category}, max size is {fmt(x)}" - ) - if category == "image" and size > MAX_IMAGE_SIZE: - raise Exception(msg(MAX_IMAGE_SIZE)) - if category == "gif" and size > MAX_GIF_SIZE: - raise Exception(msg(MAX_GIF_SIZE)) - if category == "video" and size > MAX_VIDEO_SIZE: - raise Exception(msg(MAX_VIDEO_SIZE)) - - # if is_profile: - # url = 'https://upload.twitter.com/i/media/upload.json' - # else: - # url = 'https://upload.twitter.com/1.1/media/upload.json' - - url = "https://upload.twitter.com/i/media/upload.json" - - file = Path(filename) - total_bytes = file.stat().st_size - headers = get_headers(self.session) - - upload_type = "dm" if is_dm else "tweet" - media_type = mimetypes.guess_type(file)[0] - media_category = ( - f"{upload_type}_gif" - if "gif" in media_type - else f'{upload_type}_{media_type.split("/")[0]}' - ) - - check_media(media_category, total_bytes) - - params = { - "command": "INIT", - "media_type": media_type, - "total_bytes": total_bytes, - "media_category": media_category, - } - r = self.session.post( - url=url, headers=headers, params=params, allow_redirects=True - ) - - data = self._verify_response(r) - media_id = data["media_id"] - - desc = f"uploading: {file.name}" - with tqdm( - total=total_bytes, desc=desc, unit="B", unit_scale=True, unit_divisor=1024 - ) as pbar: - with open(file, "rb") as fp: - i = 0 - while chunk := fp.read(UPLOAD_CHUNK_SIZE): - params = { - "command": "APPEND", - "media_id": media_id, - "segment_index": i, - } - try: - pad = bytes( - "".join(random.choices(ascii_letters, k=16)), - encoding="utf-8", - ) - data = b"".join( - [ - b"------WebKitFormBoundary", - pad, - b'\r\nContent-Disposition: form-data; name="media"; filename="blob"', - b"\r\nContent-Type: application/octet-stream", - b"\r\n\r\n", - chunk, - b"\r\n------WebKitFormBoundary", - pad, - b"--\r\n", - ] - ) - _headers = { - b"content-type": b"multipart/form-data; boundary=----WebKitFormBoundary" - + pad - } - self.session.post( - url=url, - headers=headers | _headers, - params=params, - content=data, - allow_redirects=True, - ) - except Exception as error: - try: - files = {"media": chunk} - self.session.post( - url=url, headers=headers, params=params, files=files - ) - except Exception as error: - return - - i += 1 - pbar.update(fp.tell() - pbar.n) - - params = {"command": "FINALIZE", "media_id": media_id, "allow_async": "true"} - if is_dm: - params |= {"original_md5": hashlib.md5(file.read_bytes()).hexdigest()} - - r = self.session.post( - url=url, headers=headers, params=params, allow_redirects=True - ) - data = self._verify_response(r) - - processing_info = data.get("processing_info") - while processing_info: - state = processing_info["state"] - if error := processing_info.get("error"): - return - if state == MEDIA_UPLOAD_SUCCEED: - break - if state == MEDIA_UPLOAD_FAIL: - return - check_after_secs = processing_info.get( - "check_after_secs", random.randint(1, 5) - ) - - time.sleep(check_after_secs) - params = {"command": "STATUS", "media_id": media_id} - - r = self.session.get( - url=url, headers=headers, params=params, allow_redirects=True - ) - data = self._verify_response(r) - processing_info = data.get("processing_info") - - return media_id - - def _add_alt_text(self, media_id: int, text: str) -> dict: - params = {"media_id": media_id, "alt_text": {"text": text}} - url = f"{self.v1_api}/media/metadata/create.json" - r = self.session.post(url, headers=get_headers(self.session), json=params) - return self._verify_response(r) - - def dm_inbox(self) -> dict: - """ - Get DM inbox metadata. - - @return: inbox as dict - """ - r = self.session.get( - f"{self.v1_api}/dm/inbox_initial_state.json", - headers=get_headers(self.session), - params=dm_params, - ) - return self._verify_response(r) - - # def dm_history(self, conversation_ids: list[str] = None) -> list[dict]: - # """ - # Get DM history. - # - # Call without arguments to get all DMS from all conversations. - # - # @param conversation_ids: optional list of conversation ids - # @return: list of messages as dicts - # """ - # - # def get(session: AsyncClient, conversation_id: str): - # params = deepcopy(dm_params) - # r = session.get( - # f"{self.v1_api}/dm/conversation/{conversation_id}.json", - # params=params, - # ) - # res = (self._verify_response(r)).get("conversation_timeline", {}) - # data = [x.get("message") for x in res.get("entries", [])] - # entry_id = res.get("min_entry_id") - # while entry_id: - # params["max_id"] = entry_id - # r = session.get( - # f"{self.v1_api}/dm/conversation/{conversation_id}.json", - # params=params, - # ) - # res = (self._verify_response(r)).get("conversation_timeline", {}) - # data.extend(x["message"] for x in res.get("entries", [])) - # entry_id = res.get("min_entry_id") - # return data - # - # def process(ids): - # limits = Limits(max_connections=100) - # headers, cookies = get_headers(self.session), self.session.cookies - # async with AsyncClient( - # limits=limits, headers=headers, cookies=cookies, timeout=20 - # ) as c: - # return tqdm_asyncio.gather( - # *(get(c, _id) for _id in ids), desc="Getting DMs" - # ) - # - # if conversation_ids: - # ids = conversation_ids - # else: - # # get all conversations - # inbox = self.dm_inbox() - # ids = list(inbox["inbox_initial_state"]["conversations"]) - # - # return asyncio.run(process(ids)) - - def dm_delete(self, *, conversation_id: str = None, message_id: str = None) -> dict: - """ - Delete operations - - - delete (hide) a single DM - - delete an entire conversation - - @param conversation_id: the conversation id - @param message_id: the message id - @return: result metadata - """ - self.session.headers.update(headers=get_headers(self.session)) - results = {"conversation": None, "message": None} - if conversation_id: - results["conversation"] = self.session.post( - f"{self.v1_api}/dm/conversation/{conversation_id}/delete.json", - ) # not json response - if message_id: - # delete single message - _id, op = Operation.DMMessageDeleteMutation - results["message"] = self.session.post( - f"{self.gql_api}/{_id}/{op}", - json={"queryId": _id, "variables": {"messageId": message_id}}, - ) - return results - - def dm_search(self, query: str) -> dict: - """ - Search DMs by keyword - - @param query: search term - @return: search results as dict - """ - - def get(cursor=None): - if cursor: - params["variables"]["cursor"] = cursor.pop() - _id, op = Operation.DmAllSearchSlice - r = self.session.get( - f"{self.gql_api}/{_id}/{op}", - params=build_params(params), - ) - res = r.json() - cursor = find_key(res, "next_cursor") - return res, cursor - - self.session.headers.update(headers=get_headers(self.session)) - variables = deepcopy(Operation.default_variables) - variables["count"] = 50 # strict limit, errors thrown if exceeded - variables["query"] = query - params = {"variables": variables, "features": Operation.default_features} - res, cursor = get() - data = [res] - while cursor: - res, cursor = get(cursor) - data.append(res) - return {"query": query, "data": data} - - def scheduled_tweets(self, ascending: bool = True) -> dict: - variables = {"ascending": ascending} - return self.gql("GET", Operation.FetchScheduledTweets, variables) - - def delete_scheduled_tweet(self, tweet_id: int) -> dict: - """duplicate, same as `unschedule_tweet()`""" - variables = {"scheduled_tweet_id": tweet_id} - return self.gql("POST", Operation.DeleteScheduledTweet, variables) - - def clear_scheduled_tweets(self) -> None: - user_id = int(re.findall('"u=(\d+)"', self.session.cookies.get("twid"))[0]) - drafts = self.gql("GET", Operation.FetchScheduledTweets, {"ascending": True}) - for _id in set(find_key(drafts, "rest_id")): - if _id != user_id: - self.gql( - "POST", Operation.DeleteScheduledTweet, {"scheduled_tweet_id": _id} - ) - - def draft_tweets(self, ascending: bool = True) -> dict: - variables = {"ascending": ascending} - return self.gql("GET", Operation.FetchDraftTweets, variables) - - def delete_draft_tweet(self, tweet_id: int) -> dict: - variables = {"draft_tweet_id": tweet_id} - return self.gql("POST", Operation.DeleteDraftTweet, variables) - - def clear_draft_tweets(self) -> None: - user_id = int(re.findall('"u=(\d+)"', self.session.cookies.get("twid"))[0]) - drafts = self.gql("GET", Operation.FetchDraftTweets, {"ascending": True}) - for _id in set(find_key(drafts, "rest_id")): - if _id != user_id: - self.gql("POST", Operation.DeleteDraftTweet, {"draft_tweet_id": _id}) - - def notifications(self, params: dict = None) -> dict: - r = self.session.get( - f"{self.v2_api}/notifications/all.json", - headers=get_headers(self.session), - params=params or live_notification_params, - ) - return self._verify_response(r) - - def recommendations(self, params: dict = None) -> dict: - r = self.session.get( - f"{self.v1_api}/users/recommendations.json", - headers=get_headers(self.session), - params=params or recommendations_params, - ) - return self._verify_response(r) - - def fleetline(self, params: dict = None) -> dict: - r = self.session.get( - "https://twitter.com/i/api/fleets/v1/fleetline", - headers=get_headers(self.session), - params=params or {}, - ) - return self._verify_response(r) - - @property - def id(self) -> int: - """Get User ID""" - return int(re.findall('"u=(\d+)"', self.session.cookies.get("twid"))[0]) - - def save_cookies(self, fname: str = None): - """Save cookies to file""" - cookies = self.session.cookies - Path(f'{fname or cookies.get("username")}.cookies').write_bytes( - orjson.dumps(dict(cookies)) - ) diff --git a/twitter_api/constants.py b/twitter_api/constants.py deleted file mode 100644 index 643b8c3..0000000 --- a/twitter_api/constants.py +++ /dev/null @@ -1,801 +0,0 @@ -from dataclasses import dataclass - - -MAX_IMAGE_SIZE = 5_242_880 # ~5 MB -MAX_GIF_SIZE = 15_728_640 # ~15 MB -MAX_VIDEO_SIZE = 536_870_912 # ~530 MB - -UPLOAD_CHUNK_SIZE = 4 * 1024 * 1024 -MEDIA_UPLOAD_SUCCEED = "succeeded" -MEDIA_UPLOAD_FAIL = "failed" - -BLACK = "\x1b[30m" -RED = "\x1b[31m" -GREEN = "\x1b[32m" -YELLOW = "\x1b[33m" -BLUE = "\x1b[34m" -MAGENTA = "\x1b[35m" -CYAN = "\x1b[36m" -WHITE = "\x1b[37m" -BOLD = "\x1b[1m" -RESET = "\x1b[0m" - -LOG_CONFIG = { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "standard": { - "format": "%(asctime)s.%(msecs)03d [%(levelname)s] :: %(message)s", - "datefmt": "%Y-%m-%d %H:%M:%S", - }, - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "standard", - "stream": "ext://sys.stdout", - }, - "file": { - "class": "logging.FileHandler", - "level": "DEBUG", - "formatter": "standard", - "filename": "twitter.log", - "mode": "a", - }, - }, - "loggers": { - "twitter": { - "handlers": ["console", "file"], - "level": "DEBUG", - } - }, -} - -ID_MAP = { - "Followers": "^user-\d+$", - "Following": "^user-\d+$", - "UserTweets": "^tweet-\d+$", - "Likes": "^tweet-\d+$", - "UserMedia": "^tweet-\d+$", - "TweetResultByRestId": "^tweet-\d+$", - "TweetsAndReplies": "^profile-conversation-\d+-tweet-\d+$", - "TweetDetail": "^conversationthread-\d+-tweet-\d+$", # if another key after tweet-\d+, it's an ad - "Retweeters": "^user-\d+$", - "Favoriters": "^user-\d+$", -} - - -@dataclass -class SearchCategory: - Top = "Top" - Latest = "Latest" - People = "People" - Photos = "Photos" - Videos = "Videos" - - -@dataclass -class SpaceCategory: - Top = "Top" - Live = "Live" - Upcoming = "Upcoming" - - -@dataclass -class SpaceState: - Ended = "Ended" - Canceled = "Canceled" - NotStarted = "NotStarted" - PrePublished = "PrePublished" - Running = "Running" - TimedOut = "TimedOut" - - -@dataclass -class Operation: - # todo: dynamically update - SearchTimeline = ( - {"rawQuery": str, "product": str}, - "nK1dw4oV3k4w5TdtcAdSww", - "SearchTimeline", - ) - AudioSpaceById = {"id": str}, "fYAuJHiY3TmYdBmrRtIKhA", "AudioSpaceById" - AudioSpaceSearch = ( - {"filter": str, "query": str}, - "NTq79TuSz6fHj8lQaferJw", - "AudioSpaceSearch", - ) - UserByScreenName = ( - {"screen_name": str}, - "sLVLhk0bGj3MVFEKTdax1w", - "UserByScreenName", - ) - UserTweets = "HuTx74BxAnezK1gWvYY7zg", "UserTweets" - ProfileSpotlightsQuery = ( - {"screen_name": str}, - "9zwVLJ48lmVUk8u_Gh9DmA", - "ProfileSpotlightsQuery", - ) - UserByRestId = {"userId": int}, "GazOglcBvgLigl3ywt6b3Q", "UserByRestId" - UsersByRestIds = {"userIds": list}, "OJBgJQIrij6e3cjqQ3Zu1Q", "UsersByRestIds" - UserMedia = {"userId": int}, "YqiE3JL1KNgf9nSljYdxaA", "UserMedia" - UserTweetsAndReplies = ( - {"userId": int}, - "RIWc55YCNyUJ-U3HHGYkdg", - "UserTweetsAndReplies", - ) - TweetResultByRestId = ( - {"tweetId": int}, - "D_jNhjWZeRZT5NURzfJZSQ", - "TweetResultByRestId", - ) - TweetDetail = "zXaXQgfyR4GxE21uwYQSyA", "TweetDetail" - TweetStats = {"rest_id": int}, "EvbTkPDT-xQCfupPu0rWMA", "TweetStats" - Likes = {"userId": int}, "nXEl0lfN_XSznVMlprThgQ", "Likes" - Followers = {"userId": int}, "pd8Tt1qUz1YWrICegqZ8cw", "Followers" - Following = {"userId": int}, "wjvx62Hye2dGVvnvVco0xA", "Following" - Retweeters = "0BoJlKAxoNPQUHRftlwZ2w", "Retweeters" - Favoriters = "XRRjv1-uj1HZn3o324etOQ", "Favoriters" - ConnectTabTimeline = ( - {"context": dict}, - "lq02A-gEzbLefqTgD_PFzQ", - "ConnectTabTimeline", - ) - - # Account Operations - useSendMessageMutation = "MaxK2PKX1F9Z-9SwqwavTw", "useSendMessageMutation" - CreateTweet = "7TKRKCPuAGsmYde0CudbVg", "CreateTweet" - DeleteTweet = "VaenaVgh5q5ih7kvyVjgtg", "DeleteTweet" - CreateScheduledTweet = "LCVzRQGxOaGnOnYH01NQXg", "CreateScheduledTweet" - DeleteScheduledTweet = "CTOVqej0JBXAZSwkp1US0g", "DeleteScheduledTweet" - CreateRetweet = "ojPdsZsimiJrUGLR1sjUtA", "CreateRetweet" - DeleteRetweet = "iQtK4dl5hBmXewYZuEOKVw", "DeleteRetweet" - FavoriteTweet = "lI07N6Otwv1PhnEgXILM7A", "FavoriteTweet" - UnfavoriteTweet = "ZYKSe-w7KEslx3JhSIk5LA", "UnfavoriteTweet" - CreateBookmark = "aoDbu3RHznuiSkQ9aNM67Q", "CreateBookmark" - DeleteBookmark = "Wlmlj2-xzyS1GN3a6cj-mQ", "DeleteBookmark" - CreateList = "hQAsnViq2BrMLbPuQ9umDA", "CreateList" - UpdateList = "4dCEFWtxEbhnSLcJdJ6PNg", "UpdateList" - ListsPinMany = "2X4Vqu6XLneR-XZnGK5MAw", "ListsPinMany" - ListPinOne = "2pYlo-kjdXoNOZJoLzI6KA", "ListPinOne" - ListUnpinOne = "c4ce-hzx6V4heV5IzdeBkA", "ListUnpinOne" - ListAddMember = "P8tyfv2_0HzofrB5f6_ugw", "ListAddMember" - ListRemoveMember = "DBZowzFN492FFkBPBptCwg", "ListRemoveMember" - DeleteList = "UnN9Th1BDbeLjpgjGSpL3Q", "DeleteList" - EditListBanner = "Uk0ZwKSMYng56aQdeJD1yw", "EditListBanner" - DeleteListBanner = "-bOKetDVCMl20qXn7YDXIA", "DeleteListBanner" - TopicFollow = "ElqSLWFmsPL4NlZI5e1Grg", "TopicFollow" - TopicUnfollow = "srwjU6JM_ZKTj_QMfUGNcw", "TopicUnfollow" - HomeLatestTimeline = "zhX91JE87mWvfprhYE97xA", "HomeLatestTimeline" - HomeTimeline = "HCosKfLNW1AcOo3la3mMgg", "HomeTimeline" - Bookmarks = "tmd4ifV8RHltzn8ymGg1aw", "Bookmarks" - - # misc/not implemented - AdAccounts = "a8KxGfFQAmm3WxqemuqSRA", "AdAccounts" - ArticleTimeline = "o9FyvnC-xg8mVBXqL4g-rg", "ArticleTimeline" - ArticleTweetsTimeline = "x4ywSpvg6BesoDszkfbFQg", "ArticleTweetsTimeline" - AudienceEstimate = "1LYVUabJBYkPlUAWRabB3g", "AudienceEstimate" - AuthenticatedUserTFLists = "QjN8ZdavFDqxUjNn3r9cig", "AuthenticatedUserTFLists" - BirdwatchAliasSelect = "3ss48WFwGokBH_gj8t_8aQ", "BirdwatchAliasSelect" - BirdwatchCreateAppeal = "TKdL0YFsX4DMOpMKeneLvA", "BirdwatchCreateAppeal" - BirdwatchCreateNote = "36EUZZyaciVmNrq4CRZcmw", "BirdwatchCreateNote" - BirdwatchCreateRating = "bD3AEK9BMCSpRods_ng2fA", "BirdwatchCreateRating" - BirdwatchDeleteNote = "IKS_qrShkDyor6Ri1ahd9g", "BirdwatchDeleteNote" - BirdwatchDeleteRating = "OpvCOyOoQClUND66zDzrnA", "BirdwatchDeleteRating" - BirdwatchEditNotificationSettings = ( - "FLgLReVIssXjB_ui3wcrRQ", - "BirdwatchEditNotificationSettings", - ) - BirdwatchFetchAliasSelfSelectOptions = ( - "szoXMke8AZOErso908iglw", - "BirdwatchFetchAliasSelfSelectOptions", - ) - BirdwatchFetchAliasSelfSelectStatus = ( - "LUEdtkcpBlGktUtms4BvwA", - "BirdwatchFetchAliasSelfSelectStatus", - ) - BirdwatchFetchAuthenticatedUserProfile = ( - "pMbW6Y4LuS5MzlSOEqERJQ", - "BirdwatchFetchAuthenticatedUserProfile", - ) - BirdwatchFetchBirdwatchProfile = ( - "btgGtchypc3D491MJ7XXWA", - "BirdwatchFetchBirdwatchProfile", - ) - BirdwatchFetchContributorNotesSlice = ( - "t6r3Wq7wripUW9gB3FQNBw", - "BirdwatchFetchContributorNotesSlice", - ) - BirdwatchFetchGlobalTimeline = ( - "L3LftPt6fhYqoQ5Vnxm7UQ", - "BirdwatchFetchGlobalTimeline", - ) - BirdwatchFetchNotes = "ZGMhf1M7kPKMOhEk1nz0Yw", "BirdwatchFetchNotes" - BirdwatchFetchOneNote = "GO8BR2MM2WZB63cdOoC7lw", "BirdwatchFetchOneNote" - BirdwatchFetchPublicData = "9bDdJ6AL26RLkcUShEcF-A", "BirdwatchFetchPublicData" - BirdwatchProfileAcknowledgeEarnOut = ( - "cED9wJy8Nd1kZCCYuIq9zQ", - "BirdwatchProfileAcknowledgeEarnOut", - ) - BizProfileFetchUser = "6OFpJ3TH3p8JpwOSgfgyhg", "BizProfileFetchUser" - BlockedAccountsAll = "h52d1F7dumWGE1tJAhQBpg", "BlockedAccountsAll" - BlockedAccountsAutoBlock = "8w-D2OhT0jmGzXaNY--UQA", "BlockedAccountsAutoBlock" - BlockedAccountsImported = "8LDNeOEm0kA98uoDsqXvMg", "BlockedAccountsImported" - BookmarkFolderTimeline = "13H7EUATwethsj-XxX5ohw", "BookmarkFolderTimeline" - BookmarkFoldersSlice = "i78YDd0Tza-dV4SYs58kRg", "BookmarkFoldersSlice" - BookmarksAllDelete = "skiACZKC1GDYli-M8RzEPQ", "BookmarksAllDelete" - Budgets = "mbK3oSQotwcJXyQIBE3uYw", "Budgets" - CardPreviewByTweetText = "jnwTSDR-Eo_HWlSkXPcMGA", "CardPreviewByTweetText" - CheckTweetForNudge = "C2dcvh7H69JALtomErxWlA", "CheckTweetForNudge" - CombinedLists = "rIxum3avpCu7APi7mxTNjw", "CombinedLists" - CommunitiesMainDiscoveryModule = ( - "8UB2fhB8TiYIW2M6vbBFXg", - "CommunitiesMainDiscoveryModule", - ) - CommunitiesMainPageTimeline = ( - "DzcxPzkGYVQk-BD0pqAcZw", - "CommunitiesMainPageTimeline", - ) - CommunitiesMembershipsSlice = ( - "s8-oxdVsoJ3w2CFD0nFt9g", - "CommunitiesMembershipsSlice", - ) - CommunitiesMembershipsTimeline = ( - "QXo-eKTsvhpCyFotNz2u6g", - "CommunitiesMembershipsTimeline", - ) - CommunityAboutTimeline = "plOgdpBzpVVQbTOEVuRc_A", "CommunityAboutTimeline" - CommunityByRestId = "bCVwRBDPi15jrdJQ7NCENQ", "CommunityByRestId" - CommunityCreateRule = "dShPoN6voXRusgxC1uvGog", "CommunityCreateRule" - CommunityDiscoveryTimeline = "b3rceNUXWRyo5mSwVZF74Q", "CommunityDiscoveryTimeline" - CommunityEditBannerMedia = "KVkZwp8Q6xy6iyhlQE5d7Q", "CommunityEditBannerMedia" - CommunityEditName = "SKToKhvm3Z4Rir8ENCJ3YQ", "CommunityEditName" - CommunityEditPurpose = "eMat-u2kx6KocreGTAt-hA", "CommunityEditPurpose" - CommunityEditRule = "9nEl5bNcdteuPGbGCdvEFA", "CommunityEditRule" - CommunityEditTheme = "4OhW6gWJwiu-JTAgBPsU1w", "CommunityEditTheme" - CommunityHashtagsTimeline = "hril1TsnshopHbmnjdUmhQ", "CommunityHashtagsTimeline" - CommunityMemberRelationshipTypeahead = ( - "NEwac2-8ONgf0756ne8oXA", - "CommunityMemberRelationshipTypeahead", - ) - CommunityModerationKeepTweet = ( - "f_YqrHSCc1mPlG-aB7pFRw", - "CommunityModerationKeepTweet", - ) - CommunityModerationTweetCasesSlice = ( - "V-iC7tjWOlzBJ44SanqGzw", - "CommunityModerationTweetCasesSlice", - ) - CommunityRemoveBannerMedia = "lSdK1v30qVhm37rDTgHq0Q", "CommunityRemoveBannerMedia" - CommunityRemoveRule = "EI_g43Ss_Ixg0EC4K7nzlQ", "CommunityRemoveRule" - CommunityReorderRules = "VwluNMGnl5uaNZ3LnlCQ_A", "CommunityReorderRules" - CommunityTweetsRankedTimeline = ( - "P38EspBBPhAfSKPP74-s2Q", - "CommunityTweetsRankedTimeline", - ) - CommunityTweetsTimeline = "2JgHOlqfeLusxAT0yGQJjg", "CommunityTweetsTimeline" - CommunityUpdateRole = "5eq76kkUqfdCzInCtcxQOA", "CommunityUpdateRole" - CommunityUserInvite = "x8hUNaBCOV2tSalqB9cwWQ", "CommunityUserInvite" - CommunityUserRelationshipTypeahead = ( - "gi_UGcUurYp6N6p2BaLJqQ", - "CommunityUserRelationshipTypeahead", - ) - ConversationControlChange = "hb1elGcj6769uT8qVYqtjw", "ConversationControlChange" - ConversationControlDelete = "OoMO_aSZ1ZXjegeamF9QmA", "ConversationControlDelete" - ConvertRitoSuggestedActions = ( - "2njnYoE69O2jdUM7KMEnDw", - "ConvertRitoSuggestedActions", - ) - Coupons = "R1h43jnAl2bsDoUkgZb7NQ", "Coupons" - CreateCommunity = "lRjZKTRcWuqwtYwCWGy9_w", "CreateCommunity" - CreateCustomerPortalSession = ( - "2LHXrd1uYeaMWhciZgPZFw", - "CreateCustomerPortalSession", - ) - CreateDraftTweet = "cH9HZWz_EW9gnswvA4ZRiQ", "CreateDraftTweet" - CreateNoteTweet = "Pyx6nga4XtTVhfTh1gtX1A", "CreateNoteTweet" - CreateQuickPromotion = "oDSoVgHhJxnd5IkckgPZdg", "CreateQuickPromotion" - CreateTrustedFriendsList = "2tP8XUYeLHKjq5RHvuvpZw", "CreateTrustedFriendsList" - CreateTweetDownvote = "Eo65jl-gww30avDgrXvhUA", "CreateTweetDownvote" - CreateTweetReaction = "D7M6X3h4-mJE8UB1Ap3_dQ", "CreateTweetReaction" - DataSaverMode = "xF6sXnKJfS2AOylzxRjf6A", "DataSaverMode" - DeleteBookmarkFolder = "2UTTsO-6zs93XqlEUZPsSg", "DeleteBookmarkFolder" - DeleteDraftTweet = "bkh9G3FGgTldS9iTKWWYYw", "DeleteDraftTweet" - DeletePaymentMethod = "VaaLGwK5KNLoc7wsOmp4uw", "DeletePaymentMethod" - DeleteTweetDownvote = "VNEvEGXaUAMfiExP8Tbezw", "DeleteTweetDownvote" - DeleteTweetReaction = "GKwK0Rj4EdkfwdHQMZTpuw", "DeleteTweetReaction" - DisableUserAccountLabel = "_ckHEj05gan2VfNHG6thBA", "DisableUserAccountLabel" - DisableVerifiedPhoneLabel = "g2m0pAOamawNtVIfjXNMJg", "DisableVerifiedPhoneLabel" - DismissRitoSuggestedAction = "jYvwa61cv3NwNP24iUru6g", "DismissRitoSuggestedAction" - DmAllSearchSlice = "U-QXVRZ6iddb1QuZweh5DQ", "DmAllSearchSlice" - DmGroupSearchSlice = "5zpY1dCR-8NyxQJS_CFJoQ", "DmGroupSearchSlice" - DmMutedTimeline = "lrcWa13oyrQc7L33wRdLAQ", "DmMutedTimeline" - DMMessageDeleteMutation = "BJ6DtxA2llfjnRoRjaiIiw", "DMMessageDeleteMutation" - DmNsfwMediaFilterUpdate = "of_N6O33zfyD4qsFJMYFxA", "DmNsfwMediaFilterUpdate" - DmPeopleSearchSlice = "xYSm8m5kJnzm_gFCn5GH-w", "DmPeopleSearchSlice" - EditBookmarkFolder = "a6kPp1cS1Dgbsjhapz1PNw", "EditBookmarkFolder" - EditDraftTweet = "JIeXE-I6BZXHfxsgOkyHYQ", "EditDraftTweet" - EditScheduledTweet = "_mHkQ5LHpRRjSXKOcG6eZw", "EditScheduledTweet" - EnableLoggedOutWebNotifications = ( - "BqIHKmwZKtiUBPi07jKctg", - "EnableLoggedOutWebNotifications", - ) - EnableVerifiedPhoneLabel = "C3RJFfMsb_KcEytpKmRRkw", "EnableVerifiedPhoneLabel" - EnrollCoupon = "SOyGmNGaEXcvk15s5bqDrA", "EnrollCoupon" - ExplorePage = "fkypGKlR9Xz9kLvUZDLoXw", "ExplorePage" - FeatureSettingsUpdate = "-btar_vkBwWA7s3YWfp_9g", "FeatureSettingsUpdate" - FetchDraftTweets = "ZkqIq_xRhiUme0PBJNpRtg", "FetchDraftTweets" - FetchScheduledTweets = "ITtjAzvlZni2wWXwf295Qg", "FetchScheduledTweets" - FollowersYouKnow = "RvojYJJB90VwJ0rdVhbjMQ", "FollowersYouKnow" - ForYouExplore = "wVEXnyTWzQlEsIuLq_D3tw", "ForYouExplore" - GenericTimelineById = "LZfAdxTdNolKXw6ZkoY_kA", "GenericTimelineById" - GetSafetyModeSettings = "AhxTX0lkbIos4WG53xwzSA", "GetSafetyModeSettings" - GetTweetReactionTimeline = "ihIcULrtrtPGlCuprduRrA", "GetTweetReactionTimeline" - GetUserClaims = "lFi3xnx0auUUnyG4YwpCNw", "GetUserClaims" - GraphQLError = "2V2W3HIBuMW83vEMtfo_Rg", "GraphQLError" - ImmersiveMedia = "UGQD_VslAJBJ4XzigsBYAA", "ImmersiveMedia" - JoinCommunity = "PXO-mA1KfmLqB9I6R-lOng", "JoinCommunity" - LeaveCommunity = "AtiTdhEyRN8ruNFW069ewQ", "LeaveCommunity" - ListByRestId = "wXzyA5vM_aVkBL9G8Vp3kw", "ListByRestId" - ListBySlug = "3-E3eSWorCv24kYkK3CCiQ", "ListBySlug" - ListCreationRecommendedUsers = ( - "Zf8ZwG57EKtss-rPlryIqg", - "ListCreationRecommendedUsers", - ) - ListEditRecommendedUsers = "-F4wsOirYNXjjg-ZjccQpQ", "ListEditRecommendedUsers" - ListLatestTweetsTimeline = "2TemLyqrMpTeAmysdbnVqw", "ListLatestTweetsTimeline" - ListMembers = "vA952kfgGw6hh8KatWnbqw", "ListMembers" - ListMemberships = "BlEXXdARdSeL_0KyKHHvvg", "ListMemberships" - ListOwnerships = "wQcOSjSQ8NtgxIwvYl1lMg", "ListOwnerships" - ListPins = "J0JOhmi8HSsle8LfSWv0cw", "ListPins" - ListProductSubscriptions = "wwdBYgScze0_Jnan79jEUw", "ListProductSubscriptions" - ListRankedTweetsTimeline = "07lytXX9oG9uCld1RY4b0w", "ListRankedTweetsTimeline" - ListSubscribe = "FjvrQI3k-97JIUbEE6Gxcw", "ListSubscribe" - ListSubscribers = "e57wIELAAe0fYt4Hmqsk6g", "ListSubscribers" - ListUnsubscribe = "bXyvW9HoS_Omy4ADhexj8A", "ListUnsubscribe" - ListsDiscovery = "ehnzbxPHA69pyaV2EydN1g", "ListsDiscovery" - ListsManagementPageTimeline = ( - "nhYp4n09Hi5n2hQWseQztg", - "ListsManagementPageTimeline", - ) - LiveCommerceItemsSlice = "-lnNX56S2YrZYrLzbccFAQ", "LiveCommerceItemsSlice" - ModerateTweet = "pjFnHGVqCjTcZol0xcBJjw", "ModerateTweet" - ModeratedTimeline = "hnaqw2Vok5OETdBVa_uexw", "ModeratedTimeline" - MuteList = "ZYyanJsskNUcltu9bliMLA", "MuteList" - MutedAccounts = "-G9eXTmseyiSenbqjrEG6w", "MutedAccounts" - NoteworthyAccountsPage = "3fOJzEwYMnVyzwgLTLIBkw", "NoteworthyAccountsPage" - PaymentMethods = "mPF_G9okpbZuLcD6mN8K9g", "PaymentMethods" - PinReply = "GA2_1uKP9b_GyR4MVAQXAw", "PinReply" - ProfileUserPhoneState = "5kUWP8C1hcd6omvg6HXXTQ", "ProfileUserPhoneState" - PutClientEducationFlag = "IjQ-egg0uPkY11NyPMfRMQ", "PutClientEducationFlag" - QuickPromoteEligibility = "LtpCXh66W-uXh7u7XSRA8Q", "QuickPromoteEligibility" - RemoveFollower = "QpNfg0kpPRfjROQ_9eOLXA", "RemoveFollower" - RemoveTweetFromBookmarkFolder = ( - "2Qbj9XZvtUvyJB4gFwWfaA", - "RemoveTweetFromBookmarkFolder", - ) - RequestToJoinCommunity = "6G66cW5zuxPXmHOeBOjF2w", "RequestToJoinCommunity" - RitoActionedTweetsTimeline = "px9Zbs48D-YdQPEROK6-nA", "RitoActionedTweetsTimeline" - RitoFlaggedAccountsTimeline = ( - "lMzaBZHIbD6GuPqJJQubMg", - "RitoFlaggedAccountsTimeline", - ) - RitoFlaggedTweetsTimeline = "iCuXMibh6yj9AelyjKXDeA", "RitoFlaggedTweetsTimeline" - RitoSuggestedActionsFacePile = ( - "GnQKeEdL1LyeK3dTQCS1yw", - "RitoSuggestedActionsFacePile", - ) - SetDefault = "QEMLEzEMzoPNbeauKCCLbg", "SetDefault" - SetSafetyModeSettings = "qSJIPIpf4gA7Wn21bT3D4w", "SetSafetyModeSettings" - SharingAudiospacesListeningDataWithFollowersUpdate = ( - "5h0kNbk3ii97rmfY6CdgAA", - "SharingAudiospacesListeningDataWithFollowersUpdate", - ) - SubscribeToScheduledSpace = "Sxn4YOlaAwEKjnjWV0h7Mw", "SubscribeToScheduledSpace" - SubscriptionCheckoutUrlWithEligibility = ( - "hKfOOObQr5JmfmxW0YtPvg", - "SubscriptionCheckoutUrlWithEligibility", - ) - SubscriptionProductDetails = "f0dExZDmFWFSWMCPQSAemQ", "SubscriptionProductDetails" - SubscriptionProductFeaturesFetch = ( - "Me2CVcAXxvK2WMr-Nh_Qqg", - "SubscriptionProductFeaturesFetch", - ) - SuperFollowers = "o0YtPFnd4Lk_pOQb9alCvA", "SuperFollowers" - TopicByRestId = "4OUZZOonV2h60I0wdlQb_w", "TopicByRestId" - TopicLandingPage = "mAKQjs1kyTS75VLZzuIXXw", "TopicLandingPage" - TopicNotInterested = "cPCFdDAaqRjlMRYInZzoDA", "TopicNotInterested" - TopicToFollowSidebar = "RPWVYYupHVZkJOnokbt2cw", "TopicToFollowSidebar" - TopicUndoNotInterested = "4tVnt6FoSxaX8L-mDDJo4Q", "TopicUndoNotInterested" - TopicsManagementPage = "Jvdjpe8qzsJD84BpK3qdkQ", "TopicsManagementPage" - TopicsPickerPage = "UvG-XXtWNcJN1LzF0u3ByA", "TopicsPickerPage" - TopicsPickerPageById = "t6kH4v2c_VzWKljc2yNwHA", "TopicsPickerPageById" - TrustedFriendsTypeahead = "RRnOwHttRGscWKC1zY9VRA", "TrustedFriendsTypeahead" - TweetEditHistory = "8eaWKjHszkS-G_hprUd9AA", "TweetEditHistory" - TwitterArticleByRestId = "hwrvh-Qt24lcprL-BDfqRA", "TwitterArticleByRestId" - TwitterArticleCreate = "aV-sm-IkvwplcxdYDoLZHQ", "TwitterArticleCreate" - TwitterArticleDelete = "6st-stMDc7KBqLT8KvWhHg", "TwitterArticleDelete" - TwitterArticleUpdateCoverImage = ( - "fpcVRSAsjvkwmCiN1HheqQ", - "TwitterArticleUpdateCoverImage", - ) - TwitterArticleUpdateData = "XpBTYp_QXwyZ0XT0JXCBJw", "TwitterArticleUpdateData" - TwitterArticleUpdateMedia = "3ojmmegfBC_oHyrmPhxj-g", "TwitterArticleUpdateMedia" - TwitterArticleUpdateTitle = "dvH6Ql989I4e5jWEV7HfaQ", "TwitterArticleUpdateTitle" - TwitterArticleUpdateVisibility = ( - "8M35gHyfpcy3S4UXejUGfA", - "TwitterArticleUpdateVisibility", - ) - TwitterArticlesSlice = "UUPSi_aS8_kHDFTWqSBPUA", "TwitterArticlesSlice" - UnmentionUserFromConversation = ( - "xVW9j3OqoBRY9d6_2OONEg", - "UnmentionUserFromConversation", - ) - UnmoderateTweet = "pVSyu6PA57TLvIE4nN2tsA", "UnmoderateTweet" - UnmuteList = "pMZrHRNsmEkXgbn3tOyr7Q", "UnmuteList" - UnpinReply = "iRe6ig5OV1EzOtldNIuGDQ", "UnpinReply" - UnsubscribeFromScheduledSpace = ( - "Zevhh76Msw574ZSs2NQHGQ", - "UnsubscribeFromScheduledSpace", - ) - UrtFixtures = "I_0j1mjMwv94SdS66S4pqw", "UrtFixtures" - UserAboutTimeline = "dm7ReTFJoeU0qkiZCO1E1g", "UserAboutTimeline" - UserAccountLabel = "rD5gLxVmMvtdtYU1UHWlFQ", "UserAccountLabel" - UserBusinessProfileTeamTimeline = ( - "dq1eUCn3N8v0BywlP4nT7A", - "UserBusinessProfileTeamTimeline", - ) - UserPromotableTweets = "jF-OgMv-9vAym3JaCPUnhQ", "UserPromotableTweets" - UserSessionsList = "vJ-XatpmQSG8bDch8-t9Jw", "UserSessionsList" - UserSuperFollowTweets = "1by3q8-AJWdNYhtltjlPTQ", "UserSuperFollowTweets" - Viewer = "okNaf-6AQWu2DD2H_MAoVw", "Viewer" - ViewerEmailSettings = "JpjlNgn4sLGvS6tgpTzYBg", "ViewerEmailSettings" - ViewerTeams = "D8mVcJSVv66_3NcR7fOf6g", "ViewerTeams" - ViewingOtherUsersTopicsPage = ( - "tYXo6h_rpnHXbdLUFMatZA", - "ViewingOtherUsersTopicsPage", - ) - WriteDataSaverPreferences = "H03etWvZGz41YASxAU2YPg", "WriteDataSaverPreferences" - WriteEmailNotificationSettings = ( - "2qKKYFQift8p5-J1k6kqxQ", - "WriteEmailNotificationSettings", - ) - adFreeArticleDomains = "zwTrX9CtnMvWlBXjsx95RQ", "adFreeArticleDomains" - articleNudgeDomains = "88Bu08U2ddaVVjKmmXjVYg", "articleNudgeDomains" - bookmarkTweetToFolder = "4KHZvvNbHNf07bsgnL9gWA", "bookmarkTweetToFolder" - createBookmarkFolder = "6Xxqpq8TM_CREYiuof_h5w", "createBookmarkFolder" - getAltTextPromptPreference = "PFIxTk8owMoZgiMccP0r4g", "getAltTextPromptPreference" - getCaptionsAlwaysDisplayPreference = ( - "BwgMOGpOViDS0ri7VUgglg", - "getCaptionsAlwaysDisplayPreference", - ) - timelinesFeedback = "vfVbgvTPTQ-dF_PQ5lD1WQ", "timelinesFeedback" - updateAltTextPromptPreference = ( - "aQKrduk_DA46XfOQDkcEng", - "updateAltTextPromptPreference", - ) - updateCaptionsAlwaysDisplayPreference = ( - "uCUQhvZ5sJ9qHinRp6CFlQ", - "updateCaptionsAlwaysDisplayPreference", - ) - - default_variables = { - "count": 1000, - "withSafetyModeUserFields": True, - "includePromotedContent": True, - "withQuickPromoteEligibilityTweetFields": True, - "withVoice": True, - "withV2Timeline": True, - "withDownvotePerspective": False, - "withBirdwatchNotes": True, - "withCommunity": True, - "withSuperFollowsUserFields": True, - "withReactionsMetadata": False, - "withReactionsPerspective": False, - "withSuperFollowsTweetFields": True, - "isMetatagsQuery": False, - "withReplays": True, - "withClientEventToken": False, - "withAttachments": True, - "withConversationQueryHighlights": True, - "withMessageQueryHighlights": True, - "withMessages": True, - } - default_features = { - "blue_business_profile_image_shape_enabled": True, - "creator_subscriptions_tweet_preview_api_enabled": True, - "freedom_of_speech_not_reach_fetch_enabled": True, - "graphql_is_translatable_rweb_tweet_is_translatable_enabled": True, - "graphql_timeline_v2_bookmark_timeline": True, - "hidden_profile_likes_enabled": True, - "highlights_tweets_tab_ui_enabled": True, - "interactive_text_enabled": True, - "longform_notetweets_consumption_enabled": True, - "longform_notetweets_inline_media_enabled": True, - "longform_notetweets_rich_text_read_enabled": True, - "longform_notetweets_richtext_consumption_enabled": True, - "profile_foundations_tweet_stats_enabled": True, - "profile_foundations_tweet_stats_tweet_frequency": True, - "responsive_web_birdwatch_note_limit_enabled": True, - "responsive_web_edit_tweet_api_enabled": True, - "responsive_web_enhance_cards_enabled": False, - "responsive_web_graphql_exclude_directive_enabled": True, - "responsive_web_graphql_skip_user_profile_image_extensions_enabled": False, - "responsive_web_graphql_timeline_navigation_enabled": True, - "responsive_web_media_download_video_enabled": False, - "responsive_web_text_conversations_enabled": False, - "responsive_web_twitter_article_data_v2_enabled": True, - "responsive_web_twitter_article_tweet_consumption_enabled": False, - "responsive_web_twitter_blue_verified_badge_is_enabled": True, - "rweb_lists_timeline_redesign_enabled": True, - "spaces_2022_h2_clipping": True, - "spaces_2022_h2_spaces_communities": True, - "standardized_nudges_misinfo": True, - "subscriptions_verification_info_verified_since_enabled": True, - "tweet_awards_web_tipping_enabled": False, - "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": True, - "tweetypie_unmention_optimization_enabled": True, - "verified_phone_label_enabled": False, - "vibe_api_enabled": True, - "view_counts_everywhere_api_enabled": True, - } - - -trending_params = { - "include_profile_interstitial_type": "1", - "include_blocking": "1", - "include_blocked_by": "1", - "include_followed_by": "1", - "include_want_retweets": "1", - "include_mute_edge": "1", - "include_can_dm": "1", - "include_can_media_tag": "1", - "include_ext_has_nft_avatar": "1", - "include_ext_is_blue_verified": "1", - "include_ext_verified_type": "1", - "skip_status": "1", - "cards_platform": "Web-12", - "include_cards": "1", - "include_ext_alt_text": "true", - "include_ext_limited_action_results": "false", - "include_quote_count": "true", - "include_reply_count": "1", - "tweet_mode": "extended", - "include_ext_views": "true", - "include_entities": "true", - "include_user_entities": "true", - "include_ext_media_color": "true", - "include_ext_media_availability": "true", - "include_ext_sensitive_media_warning": "true", - "include_ext_trusted_friends_metadata": "true", - "send_error_codes": "true", - "simple_quoted_tweet": "true", - "count": 1000, - "requestContext": "launch", - "include_page_configuration": "true", - "initial_tab_id": "trending", - "entity_tokens": "false", - "ext": "mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,birdwatchPivot,enrichments,superFollowMetadata,unmentionInfo,editControl,vibe", -} - -account_settings = { - "address_book_live_sync_enabled": False, - "allow_ads_personalization": False, - "allow_authenticated_periscope_requests": True, - "allow_dm_groups_from": "following", - "allow_dms_from": "following", # all - "allow_location_history_personalization": False, - "allow_logged_out_device_personalization": False, - "allow_media_tagging": "none", # all, following - "allow_sharing_data_for_third_party_personalization": False, - "alt_text_compose_enabled": None, - "always_use_https": True, - "autoplay_disabled": False, - "country_code": "us", - "discoverable_by_email": False, - "discoverable_by_mobile_phone": False, - "display_sensitive_media": True, - "dm_quality_filter": "enabled", # disabled - "dm_receipt_setting": "all_disabled", # all_enabled - "geo_enabled": False, - "include_alt_text_compose": True, - "include_mention_filter": True, - "include_nsfw_admin_flag": True, - "include_nsfw_user_flag": True, - "include_ranked_timeline": True, - "language": "en", - "mention_filter": "unfiltered", - "nsfw_admin": False, - "nsfw_user": False, - "personalized_trends": True, - "protected": False, - "ranked_timeline_eligible": None, - "ranked_timeline_setting": None, - "require_password_login": False, - "requires_login_verification": False, - "settings_metadata": {}, - "sleep_time": {"enabled": False, "end_time": None, "start_time": None}, - "translator_type": "none", - "universal_quality_filtering_enabled": "enabled", - "use_cookie_personalization": False, - ## todo: not yet implemented - requires additional steps - # 'allow_contributor_request': 'all', - # 'protect_password_reset': False, -} -follower_notification_settings = { - "cursor": "-1", - "include_profile_interstitial_type": "1", - "include_blocking": "1", - "include_blocked_by": "1", - "include_followed_by": "1", - "include_want_retweets": "1", - "include_mute_edge": "1", - "include_can_dm": "1", - "include_can_media_tag": "1", - "include_ext_has_nft_avatar": "1", - "include_ext_is_blue_verified": "1", - "include_ext_verified_type": "1", - "skip_status": "1", -} - -follow_settings = { - "include_profile_interstitial_type": "1", - "include_blocking": "1", - "include_blocked_by": "1", - "include_followed_by": "1", - "include_want_retweets": "1", - "include_mute_edge": "1", - "include_can_dm": "1", - "include_can_media_tag": "1", - "include_ext_has_nft_avatar": "1", - "include_ext_is_blue_verified": "1", - "include_ext_verified_type": "1", - "skip_status": "1", -} - -account_search_settings = { - "optInFiltering": True, # filter out nsfw content - "optInBlocking": True, # filter out blocked accounts -} - -profile_settings = { - "birthdate_day": int, - "birthdate_month": int, - "birthdate_year": int, # 1985 - "birthdate_visibility": str, # 'self', - "birthdate_year_visibility": str, # 'self', - "displayNameMaxLength": int, # '50', - "url": str, # 'https://example.com', - "name": str, # 'foo', - "description": str, # 'bar', - "location": str, # 'world', -} - -search_config = { - "include_profile_interstitial_type": 1, - "include_blocking": 1, - "include_blocked_by": 1, - "include_followed_by": 1, - "include_want_retweets": 1, - "include_mute_edge": 1, - "include_can_dm": 1, - "include_can_media_tag": 1, - "include_ext_has_nft_avatar": 1, - "include_ext_is_blue_verified": 1, - "include_ext_verified_type": 1, - "skip_status": 1, - "cards_platform": "Web-12", - "include_cards": 1, - "include_ext_alt_text": "true", - "include_ext_limited_action_results": "false", - "include_quote_count": "true", - "include_reply_count": 1, - "tweet_mode": "extended", - "include_ext_collab_control": "true", - "include_ext_views": "true", - "include_entities": "true", - "include_user_entities": "true", - "include_ext_media_color": "true", - "include_ext_media_availability": "true", - "include_ext_sensitive_media_warning": "true", - "include_ext_trusted_friends_metadata": "true", - "send_error_codes": "true", - "simple_quoted_tweet": "true", - "query_source": "typed_query", - "count": 1000, - "q": "", - "requestContext": "launch", - "pc": 1, - "spelling_corrections": 1, - "include_ext_edit_control": "true", - "ext": "mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,birdwatchPivot,enrichments,superFollowMetadata,unmentionInfo,editControl,collab_control,vibe", -} - -dm_params = { - "context": "FETCH_DM_CONVERSATION", - "include_profile_interstitial_type": "1", - "include_blocking": "1", - "include_blocked_by": "1", - "include_followed_by": "1", - "include_want_retweets": "1", - "include_mute_edge": "1", - "include_can_dm": "1", - "include_can_media_tag": "1", - "include_ext_has_nft_avatar": "1", - "include_ext_is_blue_verified": "1", - "include_ext_verified_type": "1", - "include_ext_profile_image_shape": "1", - "skip_status": "1", - "dm_secret_conversations_enabled": "false", - "krs_registration_enabled": "true", - "cards_platform": "Web-12", - "include_cards": "1", - "include_ext_alt_text": "true", - "include_ext_limited_action_results": "false", - "include_quote_count": "true", - "include_reply_count": "1", - "tweet_mode": "extended", - "include_ext_views": "true", - "dm_users": "false", - "include_groups": "true", - "include_inbox_timelines": "true", - "include_ext_media_color": "true", - "supports_reactions": "true", - "include_conversation_info": "true", - "ext": "mediaColor,altText,mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl", -} - -live_notification_params = params = { - "cards_platform": "Web-12", - "count": "50", # max value - "ext": "mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl", - "include_blocked_by": "1", - "include_blocking": "1", - "include_can_dm": "1", - "include_can_media_tag": "1", - "include_cards": "1", - "include_entities": "true", - "include_ext_alt_text": "true", - "include_ext_has_nft_avatar": "1", - "include_ext_is_blue_verified": "1", - "include_ext_limited_action_results": "true", - "include_ext_media_availability": "true", - "include_ext_media_color": "true", - "include_ext_profile_image_shape": "1", - "include_ext_sensitive_media_warning": "true", - "include_ext_trusted_friends_metadata": "true", - "include_ext_verified_type": "1", - "include_ext_views": "true", - "include_followed_by": "1", - "include_mute_edge": "1", - "include_profile_interstitial_type": "1", - "include_quote_count": "true", - "include_reply_count": "1", - "include_user_entities": "true", - "include_want_retweets": "1", - "send_error_codes": "true", - "simple_quoted_tweet": "true", - "skip_status": "1", - "tweet_mode": "extended", -} - -recommendations_params = { - "include_profile_interstitial_type": "1", - "include_blocking": "1", - "include_blocked_by": "1", - "include_followed_by": "1", - "include_want_retweets": "1", - "include_mute_edge": "1", - "include_can_dm": "1", - "include_can_media_tag": "1", - "include_ext_has_nft_avatar": "1", - "include_ext_is_blue_verified": "1", - "include_ext_verified_type": "1", - "include_ext_profile_image_shape": "1", - "skip_status": "1", - "pc": "true", - "display_location": "profile_accounts_sidebar", - "limit": 100, - "ext": "mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl", -} diff --git a/twitter_api/errors.py b/twitter_api/errors.py deleted file mode 100644 index 164289e..0000000 --- a/twitter_api/errors.py +++ /dev/null @@ -1,88 +0,0 @@ -class TwitterError(Exception): - """Base class for Twitter errors""" - - def __init__(self, error_dict: dict): - self.error_dict = error_dict - - @property - def error_message(self) -> str: - if self.error_code == 32: - return "Failed to authenticate account. Check your credentials." - - elif self.error_code == 36: - return "You cannot use your own user ID to report spam call" - - elif self.error_code == 38: - return "The request is missing the parameter (such as media, text, etc.) in the request." - - elif self.error_code == 50: - return "User not found." - - elif self.error_code == 89: - return "The access token used in the request is incorrect or has expired." - - elif self.error_code == 92: - return "SSL is required. Only TLS v1.2 connections are allowed in the API. Update the request to a secure connection." - - elif self.error_code == 139: - return "You have already favorited this tweet. (Duplicate)" - - elif self.error_code == 160: - return "You've already requested to follow the user. (Duplicate)" - - elif self.error_code == 186: - return "Tweet needs to be a bit shorter. The text is too long." - - elif self.error_code == 187: - return "Text of your tweet is identical to another tweet. Change your text. (Duplicate)" - - elif self.error_code == 205: - return "The account limit for reporting spam has been reached. Try again later." - - elif self.error_code == 214: - return "Account is not set up to have open Direct Messages when trying to set up a welcome message." - - elif self.error_code == 220: - return "The authentication token in use is restricted and cannot access the requested resource." - - elif self.error_code == 323: - return "Only one animated GIF may be attached to a single Post." - - elif self.error_code == 325: - return "The media ID attached to the Post was not found." - - elif self.error_code == 327: - return "You cannot repost the same Post more than once." - - elif self.error_code == 349: - return "You does not have privileges to Direct Message the recipient." - - return self.error_dict.get("error_message") - - @property - def error_code(self) -> int: - return self.error_dict.get("error_code") - - -class TwitterAccountSuspended(Exception): - """Raised when account is suspended""" - - pass - - -class CaptchaError(Exception): - """Raised when captcha solving failed""" - - pass - - -class RateLimitError(Exception): - """Raised when rate limit exceeded""" - - pass - - -class IncorrectData(Exception): - """Raised when validation error""" - - pass diff --git a/twitter_api/models/__init__.py b/twitter_api/models/__init__.py deleted file mode 100644 index e80df5c..0000000 --- a/twitter_api/models/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .tweets import * -from .users import * -from .data import * diff --git a/twitter_api/models/data/__init__.py b/twitter_api/models/data/__init__.py deleted file mode 100644 index f3e0a47..0000000 --- a/twitter_api/models/data/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .bind_account_v2 import * -from .bind_account_v1 import * diff --git a/twitter_api/models/data/bind_account_v1.py b/twitter_api/models/data/bind_account_v1.py deleted file mode 100644 index ad86ea7..0000000 --- a/twitter_api/models/data/bind_account_v1.py +++ /dev/null @@ -1,11 +0,0 @@ -from pydantic import BaseModel, field_validator, HttpUrl - - -class BindAccountParamsV1(BaseModel): - url: HttpUrl - - -class BindAccountDataV1(BaseModel): - url: HttpUrl - oauth_token: str - oauth_verifier: str diff --git a/twitter_api/models/data/bind_account_v2.py b/twitter_api/models/data/bind_account_v2.py deleted file mode 100644 index 81c9c5a..0000000 --- a/twitter_api/models/data/bind_account_v2.py +++ /dev/null @@ -1,20 +0,0 @@ -from pydantic import BaseModel, field_validator, HttpUrl - - -class BindAccountParamsV2(BaseModel): - code_challenge: str - code_challenge_method: str = "plain" - client_id: str - redirect_uri: HttpUrl - response_type: str = "code" - scope: str = "tweet.read users.read follows.read offline.access" - state: str - - @field_validator("redirect_uri", mode="after") - def validate_uri(cls, value): - # url = HttpUrl("https://google.com") - return str(value) - - -class BindAccountDataV2(BaseModel): - code: str diff --git a/twitter_api/models/tweets/__init__.py b/twitter_api/models/tweets/__init__.py deleted file mode 100644 index 1e869f4..0000000 --- a/twitter_api/models/tweets/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from .create_tweet import * -from .delete_tweet import * -from .retweet import * -from .favorite_tweet import * -from .delete_retweet import * -from .delete_favorite_tweet import * -from .bookmark_tweet import * -from .unbookmark_tweet import * -from .create_reply import * -from .scrape_replies import * -from .scrape_favorites import * -from .scrape_retweets import * -from .create_schedule_tweet import * -from .delete_unschedule_tweet import * diff --git a/twitter_api/models/tweets/bookmark_tweet.py b/twitter_api/models/tweets/bookmark_tweet.py deleted file mode 100644 index ee8118c..0000000 --- a/twitter_api/models/tweets/bookmark_tweet.py +++ /dev/null @@ -1,14 +0,0 @@ -from pydantic import BaseModel -from typing import Optional, Dict - - -class CreateBookmarkData(BaseModel): - id: str | int - - -class CreateBookmarkResult(BaseModel): - tweet_bookmark_put: Optional[str] - - -class CreateBookmarkResultData(BaseModel): - data: Optional[CreateBookmarkResult] diff --git a/twitter_api/models/tweets/create_reply.py b/twitter_api/models/tweets/create_reply.py deleted file mode 100644 index b502da5..0000000 --- a/twitter_api/models/tweets/create_reply.py +++ /dev/null @@ -1,150 +0,0 @@ -from pydantic import BaseModel -from typing import Optional, Dict, List, Any - -from .create_tweet import MediaEntity - - -class CreateReplyData(BaseModel): - id: str | int - text: str - media_entities: List[MediaEntity] | None = None - - -class Legacy(BaseModel): - can_dm: Optional[bool] - can_media_tag: Optional[bool] - created_at: Optional[str] - default_profile: Optional[bool] - default_profile_image: Optional[bool] - description: Optional[str] - entities: Optional[Dict[str, Any]] - fast_followers_count: Optional[int] - favourites_count: Optional[int] - followers_count: Optional[int] - friends_count: Optional[int] - has_custom_timelines: Optional[bool] - is_translator: Optional[bool] - listed_count: Optional[int] - location: Optional[str] - media_count: Optional[int] - name: Optional[str] - needs_phone_verification: Optional[bool] - normal_followers_count: Optional[int] - pinned_tweet_ids_str: Optional[List[str]] - possibly_sensitive: Optional[bool] - profile_image_url_https: Optional[str] - profile_interstitial_type: Optional[str] - screen_name: Optional[str] - statuses_count: Optional[int] - translator_type: Optional[str] - verified: Optional[bool] - want_retweets: Optional[bool] - withheld_in_countries: Optional[List[str]] - - -class UserResults(BaseModel): - __typename: Optional[str] - id: Optional[str] - rest_id: Optional[str] - affiliates_highlighted_label: Optional[Dict[str, Any]] - has_graduated_access: Optional[bool] - is_blue_verified: Optional[bool] - profile_image_shape: Optional[str] - legacy: Optional[Legacy] - smart_blocked_by: Optional[bool] - smart_blocking: Optional[bool] - - -class CoreUserResult(BaseModel): - result: Optional[UserResults] - - -class Core(BaseModel): - user_results: Optional[CoreUserResult] - - -class Views(BaseModel): - state: Optional[str] - - -class EditControl(BaseModel): - edit_tweet_ids: Optional[List[str]] - editable_until_msecs: Optional[str] - is_edit_eligible: Optional[bool] - edits_remaining: Optional[str] - - -class Media(BaseModel): - display_url: Optional[str] - expanded_url: Optional[str] - id_str: Optional[str] - indices: Optional[List[int]] - media_url_https: Optional[str] - type: Optional[str] - url: Optional[str] - features: Optional[Dict[str, Any]] - sizes: Optional[Dict[str, Any]] - original_info: Optional[Dict[str, int]] - - -class Entities(BaseModel): - user_mentions: Optional[List[Dict[str, Any]]] - urls: Optional[List[Any]] - hashtags: Optional[List[Any]] - symbols: Optional[List[Any]] - - -class ExtendedEntities(BaseModel): - media: Optional[List[Media]] - - -class Legacy2(BaseModel): - bookmark_count: Optional[int] - bookmarked: Optional[bool] - created_at: Optional[str] - conversation_id_str: Optional[str] - entities: Optional[Entities] - favorite_count: Optional[int] - favorited: Optional[bool] - full_text: Optional[str] - in_reply_to_screen_name: Optional[str] - in_reply_to_status_id_str: Optional[str] - in_reply_to_user_id_str: Optional[str] - is_quote_status: Optional[bool] - lang: Optional[str] - quote_count: Optional[int] - reply_count: Optional[int] - retweet_count: Optional[int] - retweeted: Optional[bool] - user_id_str: Optional[str] - id_str: Optional[str] - - -class UnmentionInfo(BaseModel): - pass - - -class CreateReplyResult(BaseModel): - rest_id: Optional[str] - has_birdwatch_notes: Optional[bool] - core: Optional[Core] - is_translatable: Optional[bool] - views: Optional[Views] - source: Optional[str] - legacy: Optional[Legacy2] - - -class CreateReplyResultDataV3(BaseModel): - result: Optional[CreateReplyResult] - - -class CreateReplyResultDataV2(BaseModel): - tweet_results: Optional[CreateReplyResultDataV3] - - -class CreateReplyResultDataV1(BaseModel): - create_tweet: Optional[CreateReplyResultDataV2] - - -class CreateReplyResultData(BaseModel): - data: Optional[CreateReplyResultDataV1] diff --git a/twitter_api/models/tweets/create_schedule_tweet.py b/twitter_api/models/tweets/create_schedule_tweet.py deleted file mode 100644 index 8e25478..0000000 --- a/twitter_api/models/tweets/create_schedule_tweet.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import List, Optional -from pydantic import BaseModel - -from .create_tweet import MediaEntity - - -class CreateScheduleTweetData(BaseModel): - text: str - date: int | str - media_entities: List[MediaEntity] | None = None - - -class CreateScheduleTweetResult(BaseModel): - rest_id: str - - -class CreateScheduleTweetResultDataV1(BaseModel): - tweet: Optional[CreateScheduleTweetResult] - - -class CreateScheduleTweetResultData(BaseModel): - data: Optional[CreateScheduleTweetResultDataV1] diff --git a/twitter_api/models/tweets/create_tweet.py b/twitter_api/models/tweets/create_tweet.py deleted file mode 100644 index decd5e3..0000000 --- a/twitter_api/models/tweets/create_tweet.py +++ /dev/null @@ -1,156 +0,0 @@ -from typing import List, Optional, Any, Dict - -from pydantic import BaseModel, field_validator -from twitter_api.errors import IncorrectData - - -class MediaEntity(BaseModel): - media_id: int - tagged_users: List[str] | None = [] - - @field_validator("tagged_users") - @classmethod - def validate_users(cls, users: List[str]): - if users: - if len(users) > 10: - raise IncorrectData("Maximum 10 tagged users allowed") - - return users - - -class CreateTweetData(BaseModel): - text: str - media_entities: List[MediaEntity] | None = None - - -class Legacy(BaseModel): - can_dm: Optional[bool] - can_media_tag: Optional[bool] - created_at: Optional[str] - default_profile: Optional[bool] - default_profile_image: Optional[bool] - description: Optional[str] - entities: Optional[Dict[str, Any]] - fast_followers_count: Optional[int] - favourites_count: Optional[int] - followers_count: Optional[int] - friends_count: Optional[int] - has_custom_timelines: Optional[bool] - is_translator: Optional[bool] - listed_count: Optional[int] - location: Optional[str] - media_count: Optional[int] - name: Optional[str] - needs_phone_verification: Optional[bool] - normal_followers_count: Optional[int] - pinned_tweet_ids_str: Optional[List[str]] - possibly_sensitive: Optional[bool] - profile_image_url_https: Optional[str] - profile_interstitial_type: Optional[str] - screen_name: Optional[str] - statuses_count: Optional[int] - translator_type: Optional[str] - verified: Optional[bool] - want_retweets: Optional[bool] - withheld_in_countries: Optional[List[str]] - - -class UserResult(BaseModel): - __typename: Optional[str] - id: Optional[str] - rest_id: Optional[str] - affiliates_highlighted_label: Optional[Dict[str, Any]] - has_graduated_access: Optional[bool] - is_blue_verified: Optional[bool] - profile_image_shape: Optional[str] - legacy: Optional[Legacy] - smart_blocked_by: Optional[bool] - smart_blocking: Optional[bool] - - -class Result(BaseModel): - result: Optional[UserResult] - - -class Core(BaseModel): - user_results: Optional[Result] - - -class Views(BaseModel): - state: Optional[str] - - -class Entities(BaseModel): - user_mentions: Optional[List[Any]] - urls: Optional[List[Any]] - hashtags: Optional[List[Any]] - symbols: Optional[List[Any]] - - -class Legacy2(BaseModel): - bookmark_count: Optional[int] - bookmarked: Optional[bool] - created_at: Optional[str] - conversation_id_str: Optional[str] - display_text_range: Optional[List[int]] - entities: Optional[Entities] - favorite_count: Optional[int] - favorited: Optional[bool] - full_text: Optional[str] - is_quote_status: Optional[bool] - lang: Optional[str] - quote_count: Optional[int] - reply_count: Optional[int] - retweet_count: Optional[int] - retweeted: Optional[bool] - user_id_str: Optional[str] - id_str: Optional[str] - - -class EditControl(BaseModel): - edit_tweet_ids: Optional[List[str]] - editable_until_msecs: Optional[str] - is_edit_eligible: Optional[bool] - edits_remaining: Optional[str] - - -class QuickPromoteEligibility(BaseModel): - eligibility: Optional[str] - - -class UnmentionData(BaseModel): - pass - - -class UnmentionInfo(BaseModel): - pass - - -class CreateTweetResult(BaseModel): - rest_id: Optional[str] - has_birdwatch_notes: Optional[bool] - core: Optional[Core] - unmention_data: Optional[UnmentionData] - edit_control: Optional[EditControl] - is_translatable: Optional[bool] - views: Optional[Views] - source: Optional[str] - legacy: Optional[Legacy2] - quick_promote_eligibility: Optional[QuickPromoteEligibility] - unmention_info: Optional[UnmentionInfo] - - -class CreateTweetResultDataV3(BaseModel): - result: Optional[CreateTweetResult] - - -class CreateTweetResultDataV2(BaseModel): - tweet_results: Optional[CreateTweetResultDataV3] - - -class CreateTweetResultDataV1(BaseModel): - create_tweet: Optional[CreateTweetResultDataV2] - - -class CreateTweetResultData(BaseModel): - data: Optional[CreateTweetResultDataV1] diff --git a/twitter_api/models/tweets/delete_favorite_tweet.py b/twitter_api/models/tweets/delete_favorite_tweet.py deleted file mode 100644 index ca941f1..0000000 --- a/twitter_api/models/tweets/delete_favorite_tweet.py +++ /dev/null @@ -1,14 +0,0 @@ -from pydantic import BaseModel -from typing import Optional - - -class DeleteFavoriteTweetData(BaseModel): - id: int | str - - -class DeleteFavoriteTweetResult(BaseModel): - unfavorite_tweet: Optional[str] - - -class DeleteFavoriteTweetResultData(BaseModel): - data: Optional[DeleteFavoriteTweetResult] diff --git a/twitter_api/models/tweets/delete_retweet.py b/twitter_api/models/tweets/delete_retweet.py deleted file mode 100644 index 57b4268..0000000 --- a/twitter_api/models/tweets/delete_retweet.py +++ /dev/null @@ -1,31 +0,0 @@ -from pydantic import BaseModel -from typing import Optional - - -class DeleteRetweetData(BaseModel): - id: int | str - - -class Legacy(BaseModel): - full_text: Optional[str] - - -class DeleteRetweetResult(BaseModel): - rest_id: Optional[str] - legacy: Optional[Legacy] - - -class DeleteRetweetResultDataV3(BaseModel): - result: Optional[DeleteRetweetResult] - - -class DeleteRetweetResultDataV2(BaseModel): - source_tweet_results: Optional[DeleteRetweetResultDataV3] - - -class DeleteRetweetResultDataV1(BaseModel): - unretweet: Optional[DeleteRetweetResultDataV2] - - -class DeleteRetweetResultData(BaseModel): - data: Optional[DeleteRetweetResultDataV1] diff --git a/twitter_api/models/tweets/delete_tweet.py b/twitter_api/models/tweets/delete_tweet.py deleted file mode 100644 index 4696fc5..0000000 --- a/twitter_api/models/tweets/delete_tweet.py +++ /dev/null @@ -1,21 +0,0 @@ -from pydantic import BaseModel - - -class DeleteTweetData(BaseModel): - id: int | str - - -class DeleteTweetResult(BaseModel): - pass - - -class DeleteTweetResultDataV2(BaseModel): - tweet_results: DeleteTweetResult - - -class DeleteTweetResultDataV1(BaseModel): - delete_tweet: DeleteTweetResultDataV2 - - -class DeleteTweetResultData(BaseModel): - data: DeleteTweetResultDataV1 diff --git a/twitter_api/models/tweets/delete_unschedule_tweet.py b/twitter_api/models/tweets/delete_unschedule_tweet.py deleted file mode 100644 index 7e3d7af..0000000 --- a/twitter_api/models/tweets/delete_unschedule_tweet.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel - - -class DeleteScheduleTweetData(BaseModel): - id: str | int - - -class DeleteScheduleTweetResult(BaseModel): - scheduledtweet_delete: Optional[str] - - -class DeleteScheduleTweetResultData(BaseModel): - data: Optional[DeleteScheduleTweetResult] diff --git a/twitter_api/models/tweets/favorite_tweet.py b/twitter_api/models/tweets/favorite_tweet.py deleted file mode 100644 index 30d6d53..0000000 --- a/twitter_api/models/tweets/favorite_tweet.py +++ /dev/null @@ -1,14 +0,0 @@ -from pydantic import BaseModel -from typing import Optional - - -class CreateFavoriteTweetData(BaseModel): - id: int | str - - -class FavoriteTweetResult(BaseModel): - favorite_tweet: Optional[str] - - -class FavoriteTweetResultData(BaseModel): - data: Optional[FavoriteTweetResult] diff --git a/twitter_api/models/tweets/retweet.py b/twitter_api/models/tweets/retweet.py deleted file mode 100644 index 3f7bb55..0000000 --- a/twitter_api/models/tweets/retweet.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel - - -class CreateRetweetData(BaseModel): - id: int | str - - -class Legacy(BaseModel): - full_text: Optional[str] - - -class RetweetResult(BaseModel): - rest_id: Optional[str] - legacy: Optional[Legacy] - - -class RetweetResultDataV3(BaseModel): - result: Optional[RetweetResult] - - -class RetweetResultDataV2(BaseModel): - retweet_results: Optional[RetweetResultDataV3] - - -class RetweetResultDataV1(BaseModel): - create_retweet: Optional[RetweetResultDataV2] - - -class RetweetResultData(BaseModel): - data: Optional[RetweetResultDataV1] diff --git a/twitter_api/models/tweets/scrape_favorites.py b/twitter_api/models/tweets/scrape_favorites.py deleted file mode 100644 index 36d1b84..0000000 --- a/twitter_api/models/tweets/scrape_favorites.py +++ /dev/null @@ -1,32 +0,0 @@ -from pydantic import BaseModel, field_validator -from twitter_api.errors import IncorrectData - - -class ScrapeTweetFavoritesData(BaseModel): - id: int | str - limit: int = 200 - - @field_validator("limit") - @classmethod - def limit_must_be_positive(cls, v): - if v < 0: - raise IncorrectData("Limit must be positive integer") - - return v - - -class UserData(BaseModel): - id: int | str - name: str - screen_name: str - profile_image_url: str - favourites_count: int - followers_count: int - friends_count: int - location: str - description: str - created_at: str - - -class ScrapeTweetFavoritesResult(BaseModel): - users: list[UserData] diff --git a/twitter_api/models/tweets/scrape_replies.py b/twitter_api/models/tweets/scrape_replies.py deleted file mode 100644 index e8d7254..0000000 --- a/twitter_api/models/tweets/scrape_replies.py +++ /dev/null @@ -1,37 +0,0 @@ -from pydantic import BaseModel, field_validator -from twitter_api.errors import IncorrectData - - -class ScrapeTweetRepliesData(BaseModel): - id: int | str - limit: int = 200 - - @field_validator("limit") - @classmethod - def limit_must_be_positive(cls, v): - if v < 0: - raise IncorrectData("Limit must be positive integer") - - return v - - -class UserData(BaseModel): - id: int | str - name: str - screen_name: str - profile_image_url: str - favourites_count: int - followers_count: int - friends_count: int - location: str - description: str - created_at: str - - -class ScrapeTweetRepliesResult(BaseModel): - reply_text: str - user_data: UserData - - -class ScrapeTweetRepliesResultData(BaseModel): - replies: list[ScrapeTweetRepliesResult] diff --git a/twitter_api/models/tweets/scrape_retweets.py b/twitter_api/models/tweets/scrape_retweets.py deleted file mode 100644 index 897a0e7..0000000 --- a/twitter_api/models/tweets/scrape_retweets.py +++ /dev/null @@ -1,20 +0,0 @@ -from pydantic import BaseModel, field_validator -from twitter_api.errors import IncorrectData -from .scrape_favorites import UserData - - -class ScrapeTweetRetweetsData(BaseModel): - id: int | str - limit: int = 200 - - @field_validator("limit") - @classmethod - def limit_must_be_positive(cls, v): - if v < 0: - raise IncorrectData("Limit must be positive integer") - - return v - - -class ScrapeTweetRetweetsResult(BaseModel): - users: list[UserData] diff --git a/twitter_api/models/tweets/unbookmark_tweet.py b/twitter_api/models/tweets/unbookmark_tweet.py deleted file mode 100644 index c4425a5..0000000 --- a/twitter_api/models/tweets/unbookmark_tweet.py +++ /dev/null @@ -1,14 +0,0 @@ -from pydantic import BaseModel -from typing import Optional, Dict - - -class DeleteBookmarkData(BaseModel): - id: str | int - - -class DeleteBookmarkResult(BaseModel): - tweet_bookmark_delete: Optional[str] - - -class DeleteBookmarkResultData(BaseModel): - data: Optional[DeleteBookmarkResult] diff --git a/twitter_api/models/users/__init__.py b/twitter_api/models/users/__init__.py deleted file mode 100644 index 4edec9c..0000000 --- a/twitter_api/models/users/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .follows import * -from .user_info import * -from .followers import * diff --git a/twitter_api/models/users/followers.py b/twitter_api/models/users/followers.py deleted file mode 100644 index def57ed..0000000 --- a/twitter_api/models/users/followers.py +++ /dev/null @@ -1,16 +0,0 @@ -from pydantic import BaseModel, field_validator -from typing import Optional, List, Dict, Any -from twitter_api.errors import IncorrectData - - -class UserFollowersData(BaseModel): - username: str - limit: int = 200 - - @field_validator("limit") - @classmethod - def limit_must_be_positive(cls, v): - if v < 0: - raise IncorrectData("Limit must be positive integer") - - return v diff --git a/twitter_api/models/users/follows.py b/twitter_api/models/users/follows.py deleted file mode 100644 index bcf448e..0000000 --- a/twitter_api/models/users/follows.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Optional, List - -from pydantic import BaseModel, model_validator -from twitter_api.errors import IncorrectData - - -class UnfollowUserData(BaseModel): - id: int | str = None - username: str = None - - @model_validator(mode="before") - @classmethod - def validate_data(cls, values: dict): - if not values.get("id") and not values.get("username"): - raise IncorrectData("Either id or username must be provided") - - return values - - -class FollowUserData(BaseModel): - id: str | int = None - username: str = None - - @model_validator(mode="before") - @classmethod - def validate_data(cls, values: dict): - if not values.get("id") and not values.get("username"): - raise IncorrectData("Either id or username must be provided") - - return values - - -class FollowsUserResult(BaseModel): - id: Optional[int] - id_str: Optional[str] - name: Optional[str] - screen_name: Optional[str] - location: Optional[str] - description: Optional[str] - url: Optional[str] - protected: Optional[bool] - followers_count: Optional[int] - fast_followers_count: Optional[int] - normal_followers_count: Optional[int] - friends_count: Optional[int] - listed_count: Optional[int] - created_at: Optional[str] - favourites_count: Optional[int] - utc_offset: Optional[int] - time_zone: Optional[str] - geo_enabled: Optional[bool] - verified: Optional[bool] - statuses_count: Optional[int] - media_count: Optional[int] - lang: Optional[str] - profile_image_url: Optional[str] - profile_image_url_https: Optional[str] - profile_banner_url: Optional[str] - pinned_tweet_ids: Optional[List[int]] - pinned_tweet_ids_str: Optional[List[str]] - has_custom_timelines: Optional[bool] - can_dm: Optional[bool] - can_media_tag: Optional[bool] - following: Optional[bool] - follow_request_sent: Optional[bool] - blocking: Optional[bool] - business_profile_state: Optional[str] - followed_by: Optional[bool] - ext_is_blue_verified: Optional[bool] - ext_has_nft_avatar: Optional[bool] diff --git a/twitter_api/models/users/user_info.py b/twitter_api/models/users/user_info.py deleted file mode 100644 index 0e7c4d7..0000000 --- a/twitter_api/models/users/user_info.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import Optional, List, Dict, Any -from pydantic import BaseModel - - -class UserProfileInfoData(BaseModel): - username: str - - -class UserProfileInfoResult(BaseModel): - id: Optional[int] - id_str: Optional[str] - name: Optional[str] - screen_name: Optional[str] - location: Optional[str] - profile_location: Optional[Dict[str, Any]] - description: Optional[str] - url: Optional[str] - # entities: Optional[Entities] - protected: Optional[bool] - followers_count: Optional[int] - fast_followers_count: Optional[int] - normal_followers_count: Optional[int] - friends_count: Optional[int] - listed_count: Optional[int] - created_at: Optional[str] - favourites_count: Optional[int] - utc_offset: Optional[int] - time_zone: Optional[str] - geo_enabled: Optional[bool] - verified: Optional[bool] - statuses_count: Optional[int] - media_count: Optional[int] - lang: Optional[str] - # status: Optional[Status] - contributors_enabled: Optional[bool] - is_translator: Optional[bool] - is_translation_enabled: Optional[bool] - profile_background_color: Optional[str] - profile_background_image_url: Optional[str] - profile_background_image_url_https: Optional[str] - profile_background_tile: Optional[bool] - profile_image_url: Optional[str] - profile_image_url_https: Optional[str] - profile_banner_url: Optional[str] - profile_link_color: Optional[str] - profile_sidebar_border_color: Optional[str] - profile_sidebar_fill_color: Optional[str] - profile_text_color: Optional[str] - profile_use_background_image: Optional[bool] - has_extended_profile: Optional[bool] - default_profile: Optional[bool] - default_profile_image: Optional[bool] - pinned_tweet_ids: Optional[List[int]] - pinned_tweet_ids_str: Optional[List[str]] - has_custom_timelines: Optional[bool] - can_media_tag: Optional[bool] - followed_by: Optional[bool] - following: Optional[bool] - follow_request_sent: Optional[bool] - notifications: Optional[bool] - advertiser_account_type: Optional[str] - advertiser_account_service_levels: Optional[List[str]] - business_profile_state: Optional[str] - translator_type: Optional[str] - withheld_in_countries: Optional[List[str]] - require_some_consent: Optional[bool] diff --git a/twitter_api/requirements.txt b/twitter_api/requirements.txt deleted file mode 100644 index 260dd0f..0000000 --- a/twitter_api/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -pydantic -orjson -httpx -curl-cffi -tqdm \ No newline at end of file diff --git a/twitter_api/util.py b/twitter_api/util.py deleted file mode 100644 index 861f8fc..0000000 --- a/twitter_api/util.py +++ /dev/null @@ -1,296 +0,0 @@ -import random -import re -import string -import time -import orjson - -from logging import Logger -from pathlib import Path -from urllib.parse import urlsplit, urlencode, urlunsplit, parse_qs, quote -from httpx import Response, Client - -from .constants import GREEN, MAGENTA, RED, RESET, ID_MAP -from .errors import TwitterError - - -def init_session(): - client = Client( - headers={ - "authorization": "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA", - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36", - }, - follow_redirects=True, - ) - r = client.post("https://api.twitter.com/1.1/guest/activate.json").json() - client.headers.update( - { - "content-type": "application/json", - "x-guest-token": r["guest_token"], - "x-twitter-active-user": "yes", - } - ) - return client - - -def batch_ids(ids: list[int], char_limit: int = 4_500) -> list[dict]: - """To avoid 431 errors""" - length = 0 - res, batch = [], [] - for x in map(str, ids): - curr_length = len(x) - if length + curr_length > char_limit: - res.append(batch) - batch = [] - length = 0 - batch.append(x) - length += curr_length - if batch: - res.append(batch) - return res - - -def build_params(params: dict) -> dict: - return {k: orjson.dumps(v).decode() for k, v in params.items()} - - -def save_json(r: Response, path: Path, name: str, **kwargs): - try: - data = r.json() - kwargs.pop("cursor", None) - out = path / "_".join(map(str, kwargs.values())) - out.mkdir(parents=True, exist_ok=True) - (out / f"{time.time_ns()}_{name}.json").write_bytes(orjson.dumps(data)) - except Exception as e: - print(f"Failed to save data: {e}") - - -def flatten(seq: list | tuple) -> list: - flat = [] - for e in seq: - if isinstance(e, list | tuple): - flat.extend(flatten(e)) - else: - flat.append(e) - return flat - - -def get_json(res: list[Response], **kwargs) -> list: - cursor = kwargs.get("cursor") - temp = res - if any(isinstance(r, (list, tuple)) for r in res): - temp = flatten(res) - results = [] - for r in temp: - try: - data = r.json() - if cursor: - results.append([data, cursor]) - else: - results.append(data) - except Exception as e: - print("Cannot parse JSON response", e) - return results - - -def set_qs(url: str, qs: dict, update=False, **kwargs) -> str: - *_, q, f = urlsplit(url) - return urlunsplit( - ( - *_, - urlencode( - qs | parse_qs(q) if update else qs, - doseq=True, - quote_via=quote, - safe=kwargs.get("safe", ""), - ), - f, - ) - ) - - -def get_cursor(data: list | dict) -> str: - # inefficient, but need to deal with arbitrary schema - entries = find_key(data, "entries") - if entries: - for entry in entries.pop(): - entry_id = entry.get("entryId", "") - if ("cursor-bottom" in entry_id) or ("cursor-showmorethreads" in entry_id): - content = entry["content"] - if itemContent := content.get("itemContent"): - return itemContent["value"] # v2 cursor - return content["value"] # v1 cursor - - -def get_headers(session, **kwargs) -> dict: - """ - Get the headers required for authenticated requests - """ - cookies = session.cookies - headers = kwargs | { - "authorization": "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs=1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA", - # "cookie": "; ".join(f"{k}={v}" for k, v in cookies.items()), - "referer": "https://twitter.com/", - "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36", - "x-csrf-token": cookies.get("ct0", ""), - # "x-guest-token": cookies.get("guest_token", ""), - "x-twitter-auth-type": "OAuth2Session" if cookies.get("auth_token") else "", - "x-twitter-active-user": "yes", - "x-twitter-client-language": "en", - } - return dict(sorted({k.lower(): v for k, v in headers.items()}.items())) - - -def find_key(obj: any, key: str) -> list: - """ - Find all values of a given key within a nested dict or list of dicts - - Most data of interest is nested, and sometimes defined by different schemas. - It is not worth our time to enumerate all absolute paths to a given key, then update - the paths in our parsing functions every time Twitter changes their API. - Instead, we recursively search for the key here, then run post-processing functions on the results. - - @param obj: dictionary or list of dictionaries - @param key: key to search for - @return: list of values - """ - - def helper(obj: any, key: str, L: list) -> list: - if not obj: - return L - - if isinstance(obj, list): - for e in obj: - L.extend(helper(e, key, [])) - return L - - if isinstance(obj, dict) and obj.get(key): - L.append(obj[key]) - - if isinstance(obj, dict) and obj: - for k in obj: - L.extend(helper(obj[k], key, [])) - return L - - return helper(obj, key, []) - - -def log(logger: Logger, level: int, r: Response): - def stat(r, txt, data): - if level >= 1: - logger.debug(f"{r.url.path}") - if level >= 2: - logger.debug(f"{r.url}") - if level >= 3: - logger.debug(f"{txt}") - if level >= 4: - logger.debug(f"{data}") - - try: - limits = {k: v for k, v in r.headers.items() if "x-rate-limit" in k} - current_time = int(time.time()) - wait = int(r.headers.get("x-rate-limit-reset", current_time)) - current_time - remaining = limits.get("x-rate-limit-remaining") - limit = limits.get("x-rate-limit-limit") - logger.debug(f"remaining: {MAGENTA}{remaining}/{limit}{RESET} requests") - logger.debug(f"reset: {MAGENTA}{(wait / 60):.2f}{RESET} minutes") - except Exception as e: - logger.error(f"Rate limit info unavailable: {e}") - - try: - status = r.status_code - ( - txt, - data, - ) = ( - r.text, - r.json(), - ) - if "json" in r.headers.get("content-type", ""): - if data.get("errors") and not find_key(data, "instructions"): - logger.error(f"[{RED}error{RESET}] {status} {data}") - else: - logger.debug(fmt_status(status)) - stat(r, txt, data) - else: - logger.debug(fmt_status(status)) - stat(r, txt, {}) - except Exception as e: - logger.error(f"Failed to log: {e}") - - -def fmt_status(status: int) -> str: - color = None - if 200 <= status < 300: - color = GREEN - elif 300 <= status < 400: - color = MAGENTA - elif 400 <= status < 600: - color = RED - return f"[{color}{status}{RESET}]" - - -def get_ids(data: list | dict, operation: tuple) -> set: - expr = ID_MAP[operation[-1]] - return {k for k in find_key(data, "entryId") if re.search(expr, k)} - - -def dump(path: str, **kwargs): - fname, data = list(kwargs.items())[0] - out = Path(path) - out.mkdir(exist_ok=True, parents=True) - (out / f"{fname}_{time.time_ns()}.json").write_bytes( - orjson.dumps(data, option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS) - ) - - -def get_code(cls, retries=5) -> str | None: - """Get verification code from Proton Mail inbox""" - - def poll_inbox(): - inbox = cls.inbox() - for c in inbox.get("Conversations", []): - if c["Senders"][0]["Address"] == "info@twitter.com": - exprs = [ - "Your Twitter confirmation code is (.+)", - "(.+) is your Twitter verification code", - ] - if temp := list( - filter(None, (re.search(expr, c["Subject"]) for expr in exprs)) - ): - return temp[0].group(1) - - for i in range(retries + 1): - if code := poll_inbox(): - return code - if i == retries: - print(f"Max retries exceeded") - return - t = 2**i + random.random() - print(f'Retrying in {f"{t:.2f}"} seconds') - time.sleep(t) - - -def get_random_string(len_: int) -> str: - return "".join( - random.choice(string.ascii_lowercase + string.digits) for _ in range(len_) - ) - - -def get_random_number(len_: int) -> str: - return "".join(random.choice(string.digits) for _ in range(len_)) - - -def generate_random_string() -> str: - return "".join([random.choice(string.ascii_letters + "-_") for _ in range(352)]) - - -def raise_for_status(response): - http_error_msg = "" - if 400 <= response.status_code < 500: - http_error_msg = f"{response.status_code} Client Error for url {response.url}" - - elif 500 <= response.status_code < 600: - http_error_msg = f"{response.status_code} Server Error for url: {response.url}" - - if http_error_msg: - raise TwitterError({"error_message": http_error_msg}) diff --git a/utils/__init__.py b/utils/__init__.py index 15b6a64..6fb0734 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1 +1,3 @@ -from .main import * +from .console import * +from .file_utils import * +from .load_config import load_config diff --git a/utils/main.py b/utils/console.py similarity index 78% rename from utils/main.py rename to utils/console.py index a9002e9..be60554 100644 --- a/utils/main.py +++ b/utils/console.py @@ -3,7 +3,7 @@ def show_dev_info(): tprint("JamBit") - print("\033[36m" + "VERSION: " + "\033[34m" + "1.0" + "\033[34m") + print("\033[36m" + "VERSION: " + "\033[34m" + "3.0" + "\033[34m") print("\033[36m" + "Channel: " + "\033[34m" + "https://t.me/JamBitPY" + "\033[34m") print( "\033[36m" @@ -16,7 +16,7 @@ def show_dev_info(): "\033[36m" + "DONATION EVM ADDRESS: " + "\033[34m" - + "0x08e3fdbb830ee591c0533C5E58f937D312b07198" + + "0xe23380ae575D990BebB3b81DB2F90Ce7eDbB6dDa" + "\033[0m" ) print() diff --git a/utils/file_utils.py b/utils/file_utils.py new file mode 100644 index 0000000..4b20060 --- /dev/null +++ b/utils/file_utils.py @@ -0,0 +1,12 @@ +import os + +from loguru import logger + + +def export_trees_ids(results: list[tuple[str, str]]): + if not os.path.exists("./trees_results"): + with open("./config/trees_results.txt", "w") as file: + for result in results: + file.write(f"{result[0]}:{result[1]}\n") + + logger.success("Trees IDs exported to trees_results.txt") diff --git a/config/load_config.py b/utils/load_config.py similarity index 66% rename from config/load_config.py rename to utils/load_config.py index 634775c..3665513 100644 --- a/config/load_config.py +++ b/utils/load_config.py @@ -7,7 +7,7 @@ def get_accounts() -> Account: - accounts_path = os.path.join(os.path.dirname(__file__), "accounts.txt") + accounts_path = os.path.join(os.getcwd(), "config", "accounts.txt") if not os.path.exists(accounts_path): logger.error(f"File <<{accounts_path}>> does not exist") exit(1) @@ -36,7 +36,7 @@ def get_accounts() -> Account: def load_config() -> Config: - settings_path = os.path.join(os.path.dirname(__file__), "settings.yaml") + settings_path = os.path.join(os.getcwd(), "config", "settings.yaml") if not os.path.exists(settings_path): logger.error(f"File <<{settings_path}>> does not exist") exit(1) @@ -46,15 +46,19 @@ def load_config() -> Config: REQUIRED_KEYS = ( "referral_code", - "eth_rpc_url", - "sepolia_rpc_url", + "mint_rpc_url", + "arb_rpc_url", "threads", - "min_amount_to_bridge", - "max_amount_to_bridge", "min_delay_before_start", "max_delay_before_start", "spin_turntable_by_percentage_of_energy", "shuffle_accounts", + "comet_bridge_amount_min", + "comet_bridge_amount_max", + "comet_bridge_wallet", + "mint_random_all_nfts", + "delay_between_mint_min", + "delay_between_mint_max", ) for key in REQUIRED_KEYS: @@ -66,15 +70,4 @@ def load_config() -> Config: if settings["shuffle_accounts"]: random.shuffle(accounts) - return Config( - accounts=accounts, - referral_code=settings["referral_code"], - threads=settings["threads"], - min_amount_to_bridge=settings["min_amount_to_bridge"], - max_amount_to_bridge=settings["max_amount_to_bridge"], - eth_rpc_url=settings["eth_rpc_url"], - sepolia_rpc_url=settings["sepolia_rpc_url"], - min_delay_before_start=settings["min_delay_before_start"], - max_delay_before_start=settings["max_delay_before_start"], - spin_turntable_by_percentage_of_energy=settings["spin_turntable_by_percentage_of_energy"], - ) + return Config(accounts=accounts, **settings)