From 0db69bf72b0b090edc22d0e682f8f3bb4356dc3e Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Tue, 24 Jun 2025 15:34:54 +0700 Subject: [PATCH] Cleaning up --- Airdrop.fsx | 179 ------------------------------- Bot/Scripts/Airdrop.fsx | 188 --------------------------------- Bot/Scripts/GetWhitelisted.fsx | 148 -------------------------- Bot/Scripts/TransferNFT.fsx | 96 ----------------- 4 files changed, 611 deletions(-) delete mode 100644 Airdrop.fsx delete mode 100644 Bot/Scripts/Airdrop.fsx delete mode 100644 Bot/Scripts/GetWhitelisted.fsx delete mode 100644 Bot/Scripts/TransferNFT.fsx diff --git a/Airdrop.fsx b/Airdrop.fsx deleted file mode 100644 index 44c17e5..0000000 --- a/Airdrop.fsx +++ /dev/null @@ -1,179 +0,0 @@ -#load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";; - -open System -open System.IO -open Npgsql.FSharp -open dotenv.net -open Solnet.Rpc -open Solnet.Rpc.Models -open Solnet.Rpc.Builders -open Solnet.Wallet -open Solnet.Programs -open Solnet.KeyStore - -let devEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.dev.env" ])) - -let ( _ , devConnStr )= devEnv.TryGetValue("DATABASE_URL") - -let keystore = SolanaKeyStoreService() -let file = File.ReadAllText("/home/joe/.config/solana/devnet.json") -let authority = keystore.RestoreKeystore(file) - -//let rpcClient = ClientFactory.GetClient("https://still-empty-field.solana-mainnet.quiknode.pro/5b1b6b5c913ec79a20bef19d5ba5f63023e470d6/") -let rpcClient = ClientFactory.GetClient(Cluster.DevNet) - -let mintAccount = PublicKey("2iS6gcoB5VhiLC4eNB7NdcaLgEHjLrXHYpz7T2JMGBDw") -//let associatedTokenAccountOwner = PublicKey("GutKESfJw8PDMbFVqByxTr4f5TUSHUVmkf5gtsWWWqrU") - -type AirdropStatus = - | Hold = 0 - | Ready = 1 - | Pending = 2 - | Error = 3 - | PendingError = 4 - | Completed = 5 - | Verified = 6 - -let updateError wallet msg = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "msg" , Sql.string msg ] - |> Sql.query """ - UPDATE crypto SET error_msg = @msg, status = 'Error' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updatePendingError wallet msg txId = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "msg" , Sql.string msg ; "txId" , Sql.string txId ] - |> Sql.query """ - UPDATE crypto SET error_msg = @msg, pending_tx = @txId, status = 'PendingError' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updatePending wallet txId = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ] - |> Sql.query """ - UPDATE crypto SET pending_tx = @txId, status = 'Pending' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updateCompleted wallet txId = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ] - |> Sql.query """ - UPDATE crypto SET successful_tx = pending_tx, status = 'Completed' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updateVerified wallet txId amount = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ; "amount" , Sql.int amount ] - |> Sql.query """ - UPDATE crypto SET pending_tx = @txId, status = 'Verified', total_dropped = @amount WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let getTokenCountToDrop wallet = - task { - let! wl = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ] - |> Sql.query """ - SELECT has_wl, has_og FROM crypto WHERE wallet_address = @wallet; - """ - |> Sql.executeRowAsync (fun reader -> {| HasWhitelist = reader.bool "has_wl" ; HasOg = reader.bool "has_og" |}) - return (if wl.HasWhitelist then 1uL else 0uL) + (if wl.HasOg then 2uL else 0uL) - } - -// TODO: Change was_successful to status and check if attempted, pending, errored, or completed -let executeDrop (wallet : string) = - let associatedTokenAccountOwner = PublicKey(wallet) - let associatedTokenAccount = AssociatedTokenAccountProgram.DeriveAssociatedTokenAccount(associatedTokenAccountOwner, mintAccount) - let log msg = printfn $"{wallet} || {msg}" - - log $"ATA: {associatedTokenAccount.Key}" - let buildTransaction (block : LatestBlockHash) amount (targetWallet : string) = - TransactionBuilder() - .SetRecentBlockHash(block.Blockhash) - .SetFeePayer(authority.Account) - .AddInstruction(AssociatedTokenAccountProgram.CreateAssociatedTokenAccount( - authority.Account, - PublicKey(targetWallet), - mintAccount)) - .AddInstruction(TokenProgram.Transfer( - PublicKey("CRa7GCMUaB4np32XoTD2sEyCXFVfYKKF4JRPkQTwV3EY"), - associatedTokenAccount, - amount, - authority.Account)) - .Build(authority.Account); - task { -// let streamingClient = ClientFactory.GetStreamingClient("wss://still-empty-field.solana-mainnet.quiknode.pro/5b1b6b5c913ec79a20bef19d5ba5f63023e470d6/") - let streamingClient = ClientFactory.GetStreamingClient(Cluster.DevNet) - do! streamingClient.ConnectAsync() - let blockHash = rpcClient.GetLatestBlockHash() - let! amount = getTokenCountToDrop wallet - log $"Dropping {amount} tokens" - let tx = buildTransaction blockHash.Result.Value amount wallet - let! tx = rpcClient.SendTransactionAsync(tx) - if tx.ErrorData <> null then - let! _ = updateError wallet (tx.ErrorData.Logs |> String.concat "\n") - () - log $"Transaction error: {wallet}" - return () - elif String.IsNullOrWhiteSpace(tx.Result) then - let msg = $"Transaction did not have an ID but the ErrorData was null: {tx.Reason}" - let! _ = updateError wallet msg - log $"Odd Transaction Error" - return () - else - log $"Successful, now waiting for RPC" - let! _ = updatePending wallet tx.Result - let! _ = streamingClient.SubscribeSignatureAsync(tx.Result, fun _ result -> - if result.Value.Error = null then - log "RPC Finished" - task { - let! _ = updateCompleted wallet tx.Result - log "Getting Transaction and Token Balance" - let! txInfo = rpcClient.GetTransactionAsync(tx.Result) - let! tokenBalance = rpcClient.GetTokenAccountBalanceAsync(associatedTokenAccount) - - - log $"Transaction Successful? {txInfo.WasSuccessful} - Token Balance {tokenBalance.Result.Value.AmountUlong}" - if txInfo.WasSuccessful = true && tokenBalance.Result.Value.AmountUlong = amount then - let! _ = updateVerified wallet tx.Result (int amount) - () - return () - } |> Async.AwaitTask |> Async.Start - else - let msg = $"Got an error, let's check it out {result.Value.Error}" - updatePendingError wallet msg tx.Result |> Async.AwaitTask |> Async.Ignore |> Async.Start) - return () - } - -let targetWallets = - devConnStr - |> Sql.connect - |> Sql.query """ - SELECT wallet_address FROM crypto WHERE status = 'Ready'; - """ - |> Sql.execute (fun reader -> reader.string "wallet_address") - -printfn $"Got target wallets: {targetWallets.Length}" - -// "GK7rkZYrdAEpTm9n9TkHWK1T5nDXeRfVUVfcHQwSDyuJ" -let asyncs = - targetWallets - |> List.map executeDrop - |> List.map Async.AwaitTask - -Async.Parallel ( asyncs , 16 ) |> Async.StartChild - - -Console.ReadLine() |> ignore diff --git a/Bot/Scripts/Airdrop.fsx b/Bot/Scripts/Airdrop.fsx deleted file mode 100644 index 96b8969..0000000 --- a/Bot/Scripts/Airdrop.fsx +++ /dev/null @@ -1,188 +0,0 @@ -#load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";; - -open System -open System.IO -open Npgsql.FSharp -open dotenv.net -open Solnet.Rpc -open Solnet.Rpc.Models -open Solnet.Rpc.Builders -open Solnet.Wallet -open Solnet.Programs -open Solnet.KeyStore - -let devEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.dev.env" ])) - -let ( _ , devConnStr )= devEnv.TryGetValue("DATABASE_URL") -let ( _ , rpcClientUrl )= devEnv.TryGetValue("RPC_CLIENT") -let ( _ , wssClientUrl )= devEnv.TryGetValue("WSS_CLIENT") - -let keystore = SolanaKeyStoreService() -//let file = File.ReadAllText("/home/joe/.config/solana/devnet.json") -let file = File.ReadAllText("/home/joe/.config/solana/DegenzW7kWzac5zEdTWEuqVasoVMPKd16T3za2j6S3qR.json") -let authority = keystore.RestoreKeystore(file) - -//let rpcClient = ClientFactory.GetClient(Cluster.DevNet) -let rpcClient = ClientFactory.GetClient(rpcClientUrl) - -//let mintAccount = PublicKey("2iS6gcoB5VhiLC4eNB7NdcaLgEHjLrXHYpz7T2JMGBDw") -let mintAccount = PublicKey("4gN1u9LNBnvFERpDU3SrMusMJ1gKLbE9XB6EYraQkWTf") -//let ataAccount = PublicKey("CRa7GCMUaB4np32XoTD2sEyCXFVfYKKF4JRPkQTwV3EY") -let sourceAtaAccount = PublicKey("7CB459UJC3qtTYJzfC8bFNvdycQGXTPzKoEHxys8Ytfs") -//let associatedTokenAccountOwner = PublicKey("GutKESfJw8PDMbFVqByxTr4f5TUSHUVmkf5gtsWWWqrU") - -type AirdropStatus = - | Hold = 0 - | Ready = 1 - | Pending = 2 - | Error = 3 - | PendingError = 4 - | Completed = 5 - | Verified = 6 - -let updateError wallet msg = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "msg" , Sql.string msg ] - |> Sql.query """ - UPDATE crypto SET error_msg = @msg, status = 'Error' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updatePendingError wallet msg txId = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "msg" , Sql.string msg ; "txId" , Sql.string txId ] - |> Sql.query """ - UPDATE crypto SET error_msg = @msg, pending_tx = @txId, status = 'PendingError' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updatePending wallet txId = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ] - |> Sql.query """ - UPDATE crypto SET pending_tx = @txId, status = 'Pending' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updateCompleted wallet txId = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ] - |> Sql.query """ - UPDATE crypto SET successful_tx = pending_tx, status = 'Completed' WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - -let updateVerified wallet txId amount = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ; "amount" , Sql.int amount ] - |> Sql.query """ - UPDATE crypto SET pending_tx = @txId, status = 'Verified', total_dropped = @amount WHERE crypto.wallet_address = @wallet; - """ - |> Sql.executeNonQueryAsync - - -let getTokenCountToDrop wallet = - task { - let! wl = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ] - |> Sql.query """ - SELECT has_wl, has_og FROM crypto WHERE wallet_address = @wallet; - """ - |> Sql.executeRowAsync (fun reader -> {| HasWhitelist = reader.bool "has_wl" ; HasOg = reader.bool "has_og" |}) - return (if wl.HasWhitelist then 1uL else 0uL) + (if wl.HasOg then 2uL else 0uL) - } - -let executeDrop (wallet : string) = - let associatedTokenAccountOwner = PublicKey(wallet) - let associatedTokenAccount = AssociatedTokenAccountProgram.DeriveAssociatedTokenAccount(associatedTokenAccountOwner, mintAccount) - let log msg = printfn $"{wallet} || {msg}" - - log $"ATA: {associatedTokenAccount.Key}" - let buildTransaction (block : LatestBlockHash) amount (targetWallet : string) = - TransactionBuilder() - .SetRecentBlockHash(block.Blockhash) - .SetFeePayer(authority.Account) - .AddInstruction(AssociatedTokenAccountProgram.CreateAssociatedTokenAccount( - authority.Account, - PublicKey(targetWallet), - mintAccount)) - .AddInstruction(TokenProgram.Transfer( - sourceAtaAccount, - associatedTokenAccount, - amount, - authority.Account)) - .Build(authority.Account); - async { -// let streamingClient = ClientFactory.GetStreamingClient(Cluster.DevNet) - let streamingClient = ClientFactory.GetStreamingClient(wssClientUrl) - do! streamingClient.ConnectAsync() |> Async.AwaitTask - let blockHash = rpcClient.GetLatestBlockHash() - let! amount = getTokenCountToDrop wallet |> Async.AwaitTask - log $"Dropping {amount} tokens" - let tx = buildTransaction blockHash.Result.Value amount wallet - let! tx = rpcClient.SendTransactionAsync(tx) |> Async.AwaitTask - if tx.ErrorData <> null then - let! _ = updateError wallet (tx.ErrorData.Logs |> String.concat "\n") |> Async.AwaitTask - () - log $"Transaction error: {wallet}" - return () - elif String.IsNullOrWhiteSpace(tx.Result) then - let msg = $"Transaction did not have an ID but the ErrorData was null: {tx.Reason}" - let! _ = updateError wallet msg |> Async.AwaitTask - log $"Odd Transaction Error" - return () - else - log $"Successful, now waiting for RPC" - let! _ = updatePending wallet tx.Result |> Async.AwaitTask - let! _ = streamingClient.SubscribeSignatureAsync(tx.Result, fun sub result -> - if result.Value.Error = null then - log "RPC Finished" - task { - let! _ = updateCompleted wallet tx.Result - log "Getting Transaction and Token Balance" - let! txInfo = rpcClient.GetTransactionAsync(tx.Result) - let! tokenBalance = rpcClient.GetTokenAccountBalanceAsync(associatedTokenAccount) - - log $"Transaction Successful? {txInfo.WasSuccessful} - Token Balance {tokenBalance.Result.Value.AmountUlong}" - if txInfo.WasSuccessful = true && tokenBalance.Result.Value.AmountUlong = amount then - let! _ = updateVerified wallet tx.Result (int amount) - () - return () - } |> Async.AwaitTask |> Async.Start - else - let msg = $"Got an error, check the tx id" - updatePendingError wallet msg tx.Result |> Async.AwaitTask |> Async.Ignore |> Async.Start) |> Async.AwaitTask - return () - } - -let targetWallets = - devConnStr - |> Sql.connect - |> Sql.query """ - SELECT wallet_address FROM crypto WHERE status = 'Ready' AND total_dropped = 0; - """ - |> Sql.execute (fun reader -> reader.string "wallet_address") - -printfn $"Got target wallets: {targetWallets.Length}" - -let asyncs = - targetWallets - |> List.map executeDrop - |> List.chunkBySize 5 - -async { - for a in asyncs do - let! _ = Async.Parallel a - do! Async.Sleep 500 -} |> Async.Start - - -Console.ReadLine() |> ignore - diff --git a/Bot/Scripts/GetWhitelisted.fsx b/Bot/Scripts/GetWhitelisted.fsx deleted file mode 100644 index 0014d55..0000000 --- a/Bot/Scripts/GetWhitelisted.fsx +++ /dev/null @@ -1,148 +0,0 @@ -#load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";; - -open Npgsql.FSharp -open DSharpPlus -open DSharpPlus.Entities -open Solnet.Programs -open dotenv.net - -let prodEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.prod.env" ])) -let devEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.dev.env" ])) - -let ( _ , prodConnStr )= prodEnv.TryGetValue("DATABASE_URL") -let ( _ , devConnStr )= devEnv.TryGetValue("DATABASE_URL") -let ( _ , adminBotToken )= prodEnv.TryGetValue("TOKEN_ADMINBOT") - -let botConfig = DiscordConfiguration() -botConfig.TokenType <- TokenType.Bot -botConfig.Intents <- DiscordIntents.All -botConfig.Token <- adminBotToken - -printfn "Connecting" -let bot = new DiscordClient(botConfig) -bot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously - -printfn "Getting Guild" -let prodGuild = 933888229776703559uL -let guild = bot.GetGuildAsync(prodGuild) |> Async.AwaitTask |> Async.RunSynchronously -printfn "Getting Members" -let users = guild.GetAllMembersAsync() |> Async.AwaitTask |> Async.RunSynchronously -printfn $"Total Members: {users.Count}" - -let firstDrop = - users - |> Seq.filter (fun u -> u.Roles |> Seq.exists (fun role -> - role.Id = 978229149569286174uL)) - |> Seq.distinct - |> Seq.toList - -printfn $"{firstDrop.Length}" - -let changeStatus ids = - devConnStr - |> Sql.connect - |> Sql.parameters [ "ids" , Sql.stringArray ids ] - |> Sql.query """ - UPDATE crypto SET status = 'Ready' WHERE discord_id = ANY (ARRAY[@ids]::varchar[]) AND status = 'Nothing' - """ - |> Sql.executeNonQuery - -changeStatus (firstDrop |> List.map (fun m -> string m.Id) |> List.toArray) - -let whitelisted = - users - |> Seq.filter (fun u -> u.Roles |> Seq.exists (fun role -> role.Name.Contains("Confirmed"))) - |> Seq.toList - -printfn $"Total Whitelist Confirmed: {whitelisted.Length}" - -printfn "Getting Wallet Addresses:" -let walletAddresses = - prodConnStr - |> Sql.connect - |> Sql.query """ - SELECT DISTINCT discord_id, display_name, wallet_address FROM "user" WHERE wallet_address IS NOT NULL; - """ - |> Sql.execute (fun reader -> {| Id = reader.string "discord_id" |> uint64 ; Name = reader.string "display_name" ; Wallet = reader.string "wallet_address" |}) - -printfn $"Total Wallet Addresses: {walletAddresses.Length}" - -let insert did name wallet wl og = - devConnStr - |> Sql.connect - |> Sql.parameters [ "did" , Sql.string (string did) ; "name" , Sql.string name ; "wallet" , Sql.string wallet - "wl" , Sql.bool wl ; "og" , Sql.bool og ] - |> Sql.query """ - INSERT INTO crypto(discord_id, display_name, wallet_address, has_wl, has_og) VALUES (@did, @name, @wallet, @wl, @og) - ON CONFLICT (discord_id) DO - UPDATE SET wallet_address = @wallet, has_wl = @wl , has_og = @og WHERE crypto.discord_id = @did AND crypto.status = 'Nothing'; - """ - |> Sql.executeNonQuery - -let updateUserWithWallet wallet wl og = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ; "wl" , Sql.bool wl ; "og" , Sql.bool og ] - |> Sql.query """ - UPDATE crypto SET has_wl = @wl, has_og = @og WHERE crypto.wallet_address = @wallet AND crypto.status = 'Nothing'; - """ - |> Sql.executeNonQuery - -let prune ids = - devConnStr - |> Sql.connect - |> Sql.parameters [ "ids" , Sql.stringArray ids ] - |> Sql.query """ - DELETE FROM crypto WHERE discord_id = ANY (ARRAY[@ids]::varchar[]) AND status = 'Nothing' - """ - |> Sql.executeNonQuery - -let getExisting () = - devConnStr - |> Sql.connect - |> Sql.query """ - SELECT discord_id, display_name, wallet_address FROM crypto - """ - |> Sql.execute (fun reader -> {| Id = reader.string "discord_id" |> uint64 ; Name = reader.string "display_name" ; Wallet = reader.string "wallet_address" |}) - -let walletExists wallet = - let list = - devConnStr - |> Sql.connect - |> Sql.parameters [ "wallet" , Sql.string wallet ] - |> Sql.query """ - SELECT wallet_address FROM crypto WHERE wallet_address = @wallet - """ - |> Sql.execute (fun reader -> reader.string "wallet_address") - not list.IsEmpty - -printfn "Inserting" -async { - for user in whitelisted do - for wa in walletAddresses do - if wa.Id = user.Id then - let hasWL = user.Roles |> Seq.exists (fun role -> role.Name = "Whitelist Confirmed") - let hasOG = user.Roles |> Seq.exists (fun role -> role.Name = "OG Whitelist Confirmed") - try - if walletExists wa.Wallet then - let _ = updateUserWithWallet wa.Wallet hasWL hasOG - () - else - let _ = insert wa.Id wa.Name wa.Wallet hasWL hasOG - () - with ex -> printfn $"Huh? {ex.Message}" - () - () - let existing = getExisting () - let usersToFilter = - existing |> List.filter (fun wa -> (whitelisted |> List.exists (fun wl -> wl.Id = wa.Id)) |> not) - let toPrune = usersToFilter |> List.map (fun u -> u.Id |> string) |> List.toArray - let _ = prune toPrune - if toPrune.Length > 0 then - printfn $"Pruning {toPrune.Length} users: {toPrune}" - let existing = getExisting () - printfn $"Total Users: {existing.Length}" - -} |> Async.RunSynchronously - -bot.DisconnectAsync() |> Async.AwaitTask |> Async.RunSynchronously \ No newline at end of file diff --git a/Bot/Scripts/TransferNFT.fsx b/Bot/Scripts/TransferNFT.fsx deleted file mode 100644 index 900c907..0000000 --- a/Bot/Scripts/TransferNFT.fsx +++ /dev/null @@ -1,96 +0,0 @@ -#load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";; - -open Npgsql.FSharp -open dotenv.net -open System.IO -open System.Diagnostics -open System.Threading.Tasks - -let prodEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.prod.env" ])) -let ( _ , prodConnStr )= prodEnv.TryGetValue("DATABASE_URL") - -type NftTransfer = { - Id : uint64 - Name : string - NftName : string - NftAddress : string - UserWallet : string -} - -printfn "Getting Wallet Addresses:" - -let pending = - prodConnStr - |> Sql.connect - |> Sql.query """ - SELECT u.id, u.display_name, token.name, token.address, mpa.tx_wallet FROM gen_one_token token - JOIN mint_pass_assignment mpa on token.id = mpa.token_id - JOIN "user" u on mpa.recipient_id = u.id - WHERE mpa.fulfilled = false AND token.locked = false - ORDER BY mpa.created_at; - """ - |> Sql.execute (fun reader -> - { Id = reader.string "id" |> uint64 - Name = reader.string "display_name" - NftName = reader.string "name" - NftAddress = reader.string "address" - UserWallet = reader.string "tx_wallet" }) - - -let testSample = [ pending |> List.last ] - -type CommandResult = { - ExitCode: int; - StandardOutput: string; - StandardError: string -} - -let executeCommand executable args = - async { - let startInfo = ProcessStartInfo() - startInfo.FileName <- executable - - for a in args do - startInfo.ArgumentList.Add(a) - - startInfo.RedirectStandardOutput <- true - startInfo.RedirectStandardError <- true - startInfo.UseShellExecute <- false - startInfo.CreateNoWindow <- true - use p = new Process() - p.StartInfo <- startInfo - p.Start() |> ignore - - let outTask = - Task.WhenAll( - [| p.StandardOutput.ReadToEndAsync() - p.StandardError.ReadToEndAsync() |] - ) - - do! p.WaitForExitAsync() |> Async.AwaitTask - let! out = outTask |> Async.AwaitTask - - return - { ExitCode = p.ExitCode - StandardOutput = out.[0] - StandardError = out.[1] } - } -let executeShellCommand command = - executeCommand "/usr/bin/env" [ "-S"; "bash"; "-c"; command ] - - -let transfer (nft : NftTransfer) = - printfn $"Transferring {nft.NftName} to {nft.Name} " - let result = - $"spl-token transfer --allow-unfunded-recipient --fund-recipient {nft.NftAddress} 1 {nft.UserWallet}" - |> executeShellCommand - |> Async.RunSynchronously - if result.ExitCode = 0 then - let user = $"{nft.Id} - {nft.Name} - {nft.NftName}" - let tx = result.StandardOutput - File.AppendAllLines("/home/joe/Downloads/transactions", [ user ; tx ]) - else - printfn $"{result.StandardError}" - -for nft in pending do - transfer nft \ No newline at end of file