discord-bot-game/Bot/InviteTracker.fs

627 lines
26 KiB
Forth
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module Degenz.InviteTracker
open System.Text
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Messaging
open Npgsql.FSharp
let connStr = GuildEnvironment.connectionString
let InviteRewardAmount = 100<GBT>
type Invite = {
Code : string
Inviter : uint64
Count : int
}
let private mapInvite (reader : RowReader) = {
Code = reader.string "code"
Inviter = reader.string "inviter" |> uint64
Count = reader.int "count"
}
let private getInvites () = async {
let! invites =
connStr
|> Sql.connect
|> Sql.query "SELECT code, inviter, count FROM invite"
|> Sql.executeAsync mapInvite
|> Async.AwaitTask
return
invites
|> List.map (fun inv -> (inv.Code , (inv.Inviter , inv.Count)))
|> Map.ofList
}
let private getInvitesFromUser discordId = async {
let! invites =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string discordId) ]
|> Sql.query """
SELECT code, count FROM invite
WHERE inviter = @did
"""
|> Sql.executeAsync (fun read -> {
Code = read.string "code"
Inviter = discordId
Count = read.int "count"
})
|> Async.AwaitTask
return
invites
|> List.map (fun inv -> (inv.Code , (inv.Inviter , inv.Count)))
|> Map.ofList
}
let private createInvite inviter code =
connStr
|> Sql.connect
|> Sql.parameters [ "code" , Sql.string code ; "inviter" , Sql.string (string inviter) ]
|> Sql.query "INSERT INTO invite (code, inviter) VALUES (@code, @inviter)"
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
let private addInvitedUser did code count =
try
connStr
|> Sql.connect
|> Sql.executeTransactionAsync [
"""
INSERT INTO invited_user (discord_id, invite_id)
VALUES (@did, (SELECT id FROM invite WHERE code = @code));
""" , [ [ "@code" , Sql.string code ; "@did" , Sql.string (string did) ] ]
"UPDATE invite SET count = @count WHERE code = @code" , [ [ "count" , Sql.int count ; "code" , Sql.string code ] ]
]
|> Async.AwaitTask
|> Async.Ignore
with _ -> async.Zero ()
let private markInvitedAccepted did =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string did) ]
|> Sql.query "UPDATE invited_user SET accepted = true, updated_at = timezone('utc'::text, now()) WHERE discord_id = @did"
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
let private getInviteFromInvitedUser invitedUser =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string invitedUser) ]
|> Sql.query """
SELECT code, inviter, count FROM invite
JOIN invited_user iu ON invite.id = iu.invite_id
WHERE iu.discord_id = @did
"""
|> Sql.executeRowAsync mapInvite
|> Async.AwaitTask
let private removeInvitedUser did =
try
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string did) ]
|> Sql.query "DELETE FROM invited_user WHERE discord_id = @did"
|> Sql.executeNonQueryAsync
|> Async.AwaitTask
|> Async.Ignore
with _ -> async.Zero ()
let private checkUserAlreadyInvited userId = async {
let! result =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query """
SELECT id FROM invited_user WHERE discord_id = @did
"""
|> Sql.executeAsync (fun read -> read.int "id")
|> Async.AwaitTask
return List.isEmpty result |> not
}
let checkInviteAccepted (userId : uint64) = async {
try
let! result =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query "SELECT accepted FROM invited_user WHERE discord_id = @did"
|> Sql.executeRowAsync (fun read -> read.bool "accepted")
|> Async.AwaitTask
return result
with ex ->
printfn "%s %u" ex.Message userId
return false
}
let private getInviteAttributions userId =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query """
SELECT count(*) FROM invited_user
JOIN invite ON invite.id = invited_user.invite_id
WHERE invite.inviter = @did AND invited_user.accepted = true;
"""
|> Sql.executeRowAsync (fun read -> read.int "count")
|> Async.AwaitTask
let getInvitedUsers userId =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query """
WITH invite AS (SELECT id FROM invite WHERE inviter = @did)
SELECT discord_id FROM invited_user, invite
WHERE invite.id = invited_user.invite_id AND invited_user.accepted = true
ORDER BY invited_user.updated_at DESC, invited_user.created_at DESC LIMIT 10
"""
|> Sql.executeAsync (fun read -> read.string "discord_id" |> uint64)
|> Async.AwaitTask
let getInvitedUserCount userId =
connStr
|> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query """
WITH invite AS (SELECT id FROM invite WHERE inviter = @did)
SELECT count(*) FROM invited_user, invite
WHERE invite.id = invited_user.invite_id AND invited_user.accepted = true
"""
|> Sql.executeRowAsync (fun read -> read.int "count")
|> Async.AwaitTask
let guildInviteEmbed =
let rewardMsg =
$"**Your Mission:**\nCLICK THE BUTTON below, then share your **UNIQUE LINK** with any Degenz you want to invite into the Server.\n\n" +
$"**Your Reward:**\n`Earn {InviteRewardAmount} $GBT` 💰 for every Degen you invite into the server, that **COMPLETES** hacker training.\n\n" +
$"**Commands:**\n`/recruit` - Invite Degenz into the server.\n`/recruited` - Check how many Degenz you've invited."
let embed =
DiscordEmbedBuilder()
.WithColor(DiscordColor.Green)
.WithDescription(rewardMsg)
.WithImageUrl("https://s1.gifyu.com/images/whitelist-image-banner-3.gif")
.WithTitle("Recruitment")
let builder =
DiscordFollowupMessageBuilder()
.AddEmbed(embed)
.AsEphemeral(true)
let button = DiscordButtonComponent(ButtonStyle.Success, $"CreateGuildInvite", $"GET MY UNIQUE LINK") :> DiscordComponent
builder.AddComponents [| button |]
let private showInviteMessage (ctx : IDiscordContext) origin =
task {
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
match! DbService.tryFindPlayer (ctx.GetDiscordMember().Id) with
| Some player ->
match player.Active with
| true -> do! ctx.FollowUp(guildInviteEmbed)
| false -> do! sendFollowUpMessage ctx $"You're not in the game! Go to <#{GuildEnvironment.channelShelters}> NOW to get assigned a private bunk, and **JOIN THE GAME!**"
do! Analytics.recruitCommand origin player.DiscordId (ctx.GetDiscordMember().Username) (ctx.GetChannel())
| None ->
do! sendFollowUpMessage ctx $"You're not in the game! Go to <#{GuildEnvironment.channelShelters}> NOW to get assigned a private bunk, and **JOIN THE GAME!**"
} :> Task
let private listServerInvites (ctx : IDiscordContext) = task {
let! invites = ctx.GetGuild().GetInvitesAsync()
let sb = StringBuilder()
for invite in invites do
sb.AppendLine($"{invite.Inviter.Username} - {invite.Code}") |> ignore
let msg =
DiscordInteractionResponseBuilder()
.AsEphemeral(true)
.WithContent("Server Invites\n" + sb.ToString())
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let getAttributions userId (ctx : IDiscordContext) =
task {
let! total = getInviteAttributions(userId)
let msg =
DiscordInteractionResponseBuilder()
.AsEphemeral(true)
.WithContent($"<@{userId}> has invited {total} people")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
} :> Task
let getInvitedUsersForId (user : DiscordUser) (ctx : IDiscordContext) =
task {
do! Messaging.defer ctx
let! users = getInvitedUsers (user.Id)
let! total = getInvitedUserCount (user.Id)
let sb = StringBuilder()
let mutable count = 0
for user in users do
count <- count + 1
sb.AppendLine($"{count}.) <@{user}>") |> ignore
let msg =
let str =
if users.Length > 0 then
$"**Total Recruited:** `{total} Degenz`\n**Total Earned:** `{total * InviteRewardAmount} 💰$GBT`\n\n**Last 10 users recruited:**\n{sb}"
else
$"You haven't recruited anyone yet, use the `/recruit` command to get the recruitment link"
DiscordFollowupMessageBuilder()
.AsEphemeral(true)
.WithContent(str)
do! ctx.FollowUp(msg)
let user = ctx.GetDiscordMember()
do! Analytics.recruitedCommand total user.Id user.Username (ctx.GetChannel())
} :> Task
let clearInvites (ctx : IDiscordContext) = task {
let! invites = ctx.GetGuild().GetInvitesAsync()
do!
invites
|> Seq.map (fun invite -> invite.DeleteAsync() |> Async.AwaitTask)
|> Async.Sequential
|> Async.Ignore
}
// Discord doesn't have any way to tell you if the user came via an invite, the only way to tell is to compare the
// cached invites in the DB to the ones in the guild and see if any has been incremented
let private processNewUser (eventArgs : GuildMemberAddEventArgs) =
task {
let! guildInvites = eventArgs.Guild.GetInvitesAsync()
let! cachedInvites = getInvites ()
for invite in guildInvites do
let result = cachedInvites.TryFind(invite.Code)
match result with
| Some (_,count) ->
if invite.Uses > count then
do! addInvitedUser eventArgs.Member.Id invite.Code invite.Uses |> Async.Ignore
do! Analytics.invitedUserEntered invite.Code invite.Inviter.Id eventArgs.Member.Id invite.Inviter.Username eventArgs.Member.Username
| None -> ()
} :> Task
let acceptInvite (guild : DiscordGuild) discordId memberName =
task {
match! checkInviteAccepted discordId with
| false ->
let! _ = markInvitedAccepted discordId |> Async.Ignore
try
let! invite = getInviteFromInvitedUser discordId
let! player = DbService.tryFindPlayer invite.Inviter
match player with
| Some player ->
do! DbService.updatePlayerCurrency InviteRewardAmount player.DiscordId |> Async.Ignore
do! match GuildEnvironment.botClientRecruit with
| Some recruitBot -> async {
let builder = DiscordMessageBuilder()
builder.WithContent($"{memberName} was recruited and is now a Degen. <@{player.DiscordId}> just earned {InviteRewardAmount} 💰$GBT for their efforts!") |> ignore
let channel = guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)
do! recruitBot.SendMessageAsync(channel, builder)
|> Async.AwaitTask
|> Async.Ignore
}
| None -> async.Return()
do! Analytics.invitedUserAccepted invite.Code player.DiscordId discordId player.Name memberName
| None -> return ()
with _ -> ()
| true -> return ()
} :> Task
let sendInitialEmbed (ctx : IDiscordContext) =
async {
try
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelWhitelist)
let builder = DiscordMessageBuilder()
let embed = DiscordEmbedBuilder()
embed.ImageUrl <- "https://s1.gifyu.com/images/whitelist-image-2.gif"
embed.Title <- "Degenz Game"
embed.Color <- DiscordColor.White
embed.Description <- """
Mint Date: **May 2022**
Supply: **3,333**
Price: **1.984 $SOL**
Your NFT may be your In-Game Character that provides you with unique traits, and abilities in game.
"""
builder.AddEmbed embed |> ignore
let button = DiscordButtonComponent(ButtonStyle.Success, $"GimmeWhitelist", $"Give Me Whitelist") :> DiscordComponent
builder.AddComponents [| button |] |> ignore
do! GuildEnvironment.botClientRecruit.Value.SendMessageAsync(channel, builder)
|> Async.AwaitTask
|> Async.Ignore
with e ->
printfn $"Error trying to get channel Whitelist\n\n{e.Message}"
} |> Async.RunSynchronously
type WhitelistResult =
| NotInGame
| NotAHacker
| NotEnoughGBT of currentAmount : int<GBT>
| NotEnoughStock
| Granted of PlayerData
| AlreadyWhitelisted
let tryGrantWhitelist (ctx : IDiscordContext) stock price =
task {
let user = ctx.GetDiscordMember()
match! DbService.tryFindPlayer user.Id with
| Some player ->
let hasWhitelist = Seq.contains (ctx.GetGuild().GetRole(GuildEnvironment.roleWhitelist)) user.Roles
match hasWhitelist , player.Active , stock > 0 , player.Bank >= price with
| true , _ , _ , _ -> return AlreadyWhitelisted
| _ , false , _ , _ -> return NotInGame
| _ , _ , false , _ -> return NotEnoughStock
| _ , _ , _ , false -> return NotEnoughGBT player.Bank
| _ , _ , _ , _ -> return Granted player
| None -> return NotInGame
}
let notAHackerMsg = $"""
Woah slow down buddy… Youre not even a hacker yet! To get Whitelisted you need to buy it with **$GBT** by playing the game.
Go to <#{GuildEnvironment.channelTraining}> NOW to finish training and become a **HACKER**!**
"""
let notInGameMsg = $"""
Woah slow down buddy… Youre not even in the game yet! To get Whitelisted you need to buy it with **$GBT** by playing the game.
Go to <#{GuildEnvironment.channelShelters}> NOW to get assigned a bunk, and **JOIN THE GAME!**
"""
let alreadyWhitelistedMsg = $"""
Youre **ALREADY** Whitelisted! Save some for other Degenz…
**Remember:**
Earn `100 $GBT` 💰 for every Degen you recruit into the game!
Just type `/recruit` anywhere, or press the button below...
**Commands:**
`/recruit` - Invite Degenz into the server.
`/recruited` - Check how many Degenz youve invited.
"""
let notEnoughMoneyMsg price total = $"""
Oh no!
You don't have enough **$GBT** to buy a WHITELIST spot! Come back when you have `{price - total}` more $GBT.
The QUICKEST way to earn **$GBT** is by recruiting other Degenz into the server.
Earn `{InviteRewardAmount} $GBT` 💰 for every Degen you recruit into the game!
Just type `/recruit` anywhere, anytime... Or just press the button below!
"""
let canBuyWhitelistMsg = $"""
Look at you Degen, you played Big Brothers games and made it out alive! Now you can use your $GBT to pay for one of our coveted Whitelist spots.
Click buy now below and the role will be auto assigned to you.
"""
let handleGimmeWhitelist (ctx : IDiscordContext) =
task {
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
let whitelistEmbed = DiscordEmbedBuilder()
whitelistEmbed.Title <- "1x Degenz Game Whitelist "
let includeInfo stock price =
whitelistEmbed.ImageUrl <- "https://s7.gifyu.com/images/whitelist-item-mock-banner18.png"
whitelistEmbed.AddField("Item", "`1x Whitelist`", true) |> ignore
whitelistEmbed.AddField("Available", $"`{stock}`", true) |> ignore
whitelistEmbed.AddField("Price 💰", $"`{price} $GBT`", true) |> ignore
whitelistEmbed.Color <- DiscordColor.Red
let buyBtn = DiscordButtonComponent(ButtonStyle.Success, $"BuyWhitelist", $"Buy Now", true) :> DiscordComponent
let buyActiveBtn = DiscordButtonComponent(ButtonStyle.Success, $"BuyWhitelist", $"Buy Now") :> DiscordComponent
let recruitBtn = DiscordButtonComponent(ButtonStyle.Danger, $"ShowRecruitmentEmbed", $"Recruit Now") :> DiscordComponent
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
let! wlItem = DbService.getStoreItemBySymbol "WHITELIST"
let wlPrice = Inventory.getBuyPrice wlItem.Item
let! availability = tryGrantWhitelist ctx wlItem.Stock wlPrice
match availability with
| NotAHacker -> whitelistEmbed.Description <- notAHackerMsg
| NotInGame -> whitelistEmbed.Description <- notInGameMsg
| AlreadyWhitelisted ->
builder.AddComponents([ recruitBtn ]) |> ignore
whitelistEmbed.Color <- DiscordColor.Green
whitelistEmbed.Color <- DiscordColor.Green
whitelistEmbed.Description <- alreadyWhitelistedMsg
| NotEnoughStock -> whitelistEmbed.Description <- "Oh no! We do not have any whitelist spots available for now. Check back later or go bother Kitty and ask him why the fuck you can't whitelist"
| NotEnoughGBT total ->
includeInfo wlItem.Stock wlPrice
builder.AddComponents([ buyBtn ; recruitBtn ]) |> ignore
whitelistEmbed.Description <- notEnoughMoneyMsg wlPrice total
| Granted _ ->
includeInfo wlItem.Stock wlPrice
whitelistEmbed.Color <- DiscordColor.Green
whitelistEmbed.Color <- DiscordColor.Green
builder.AddComponents([ buyActiveBtn ]) |> ignore
whitelistEmbed.Description <- canBuyWhitelistMsg
builder.AddEmbed(whitelistEmbed) |> ignore
do! ctx.FollowUp(builder)
let availabilityStr =
match availability with
| NotEnoughGBT _ -> "NotEnoughGBT"
| Granted _ -> "Granted"
| _ -> string availability
let user = ctx.GetDiscordMember()
do! Analytics.whiteListButton availabilityStr user.Id user.Username
} :> Task
let buyWhitelistMsg = $"""
🎉 Congratulations youve been **WHITELISTED!**
**Remember:**
Earn `100 $GBT` 💰 for every Degen you recruit into the game!
Just type `/recruit` anywhere, or press the button below...
**Commands:**
`/recruit` - Invite Degenz into the server.
`/recruited` - Check how many Degenz youve invited.
"""
let handleBuyWhitelist (ctx : IDiscordContext) =
task {
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
let! wlItem = DbService.getStoreItemBySymbol "WHITELIST"
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
let wlPrice = Inventory.getBuyPrice wlItem.Item
match! tryGrantWhitelist ctx wlItem.Stock wlPrice with
| NotAHacker ->
builder.Content <- $"You are somehow not a hacker anymore, what exactly are you doing?"
do! ctx.FollowUp(builder)
| NotInGame ->
builder.Content <- $"You somehow have left the game, what exactly are you doing?"
do! ctx.FollowUp(builder)
| AlreadyWhitelisted ->
builder.Content <- "🎉 You're already WHITELISTED!"
do! ctx.FollowUp(builder)
| NotEnoughGBT _ ->
builder.Content <- $"You somehow do not have enough $GBT, what exactly are you doing?"
do! ctx.FollowUp(builder)
| NotEnoughStock ->
builder.Content <- $"We just ran out of stock, tough shit"
do! ctx.FollowUp(builder)
| Granted player ->
match! DbService.decrementItemStock wlItem.Item with
| true ->
let embed = DiscordEmbedBuilder()
embed.Description <- buyWhitelistMsg
embed.Color <- DiscordColor.Green
let recruitBtn = DiscordButtonComponent(ButtonStyle.Danger, $"ShowRecruitmentEmbed", $"Recruit Now") :> DiscordComponent
builder.AddComponents [ recruitBtn ] |> ignore
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleWhitelist)
do! ctx.GetDiscordMember().GrantRoleAsync(role)
let! _ = DbService.updatePlayerCurrency -wlPrice player.DiscordId
builder.AddEmbed(embed) |> ignore
do! ctx.FollowUp(builder)
// Send message to hall of privacy
let builder = DiscordMessageBuilder()
builder.WithContent($"{player.Name} just purchased WHITELIST!") |> ignore
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle)
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
let user = ctx.GetDiscordMember()
do! Analytics.whiteListPurchased wlPrice wlItem.Stock user.Id user.Username
| false ->
let embed = DiscordEmbedBuilder()
embed.Description <- "Oh no! Looks like the last Whitelist spot was taken. Don't worry you weren't charged..."
embed.Color <- DiscordColor.Red
builder.AddEmbed(embed) |> ignore
do! ctx.FollowUp(builder)
} :> Task
let handleCreateInvite (ctx : IDiscordContext) =
task {
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
let user = ctx.GetDiscordMember()
let! code =
task {
let! invites = getInvitesFromUser user.Id
match invites |> Map.toList with
| [] ->
let ( result , channel ) = ctx.GetGuild().Channels.TryGetValue(GuildEnvironment.channelWelcome)
if result then
let! invite = channel.CreateInviteAsync(max_age = 0, unique = true)
try do! createInvite (ctx.GetDiscordMember().Id) invite.Code |> Async.Ignore
with ex -> printfn "%A" ex.Message
return invite.Code
else
printfn "Error finding Welcome channel"
return ""
| invite::_ ->
return invite |> fst
}
let msg =
DiscordFollowupMessageBuilder()
.WithContent($"https://discord.gg/{code}")
.AsEphemeral(true)
do! ctx.FollowUp(msg)
do! Analytics.recruitLinkButton code user.Id user.Username (ctx.GetChannel())
} :> Task
let handleRoleAssigned (client : DiscordClient) (event : GuildMemberUpdateEventArgs) =
let addedRole (rolesBefore : DiscordRole seq) (rolesAfter : DiscordRole seq) =
rolesAfter |> Seq.filter ((fun role -> rolesBefore |> Seq.exists (fun r -> role.Id = r.Id)) >> not)
task {
let symmetricDifference = addedRole event.RolesBefore event.RolesAfter |> Seq.toList
match symmetricDifference with
| [] -> ()
| role::_ ->
let (_,guild) = client.Guilds.TryGetValue(GuildEnvironment.guildId)
do! acceptInvite guild event.Member.Id event.Member.Username |> Async.AwaitTask
return ()
} :> Task
let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
let eventCtx = DiscordEventContext event :> IDiscordContext
match event.Id with
| id when id.StartsWith("GimmeWhitelist") -> handleGimmeWhitelist eventCtx
| id when id.StartsWith("BuyWhitelist") -> handleBuyWhitelist eventCtx
| id when id.StartsWith("CreateGuildInvite") -> handleCreateInvite eventCtx
| id when id.StartsWith("ShowRecruitmentEmbed") -> showInviteMessage eventCtx "RecruitButton"
| _ ->
task {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"Incorrect Action identifier {eventCtx.GetInteractionId()}"
do! eventCtx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
}
let handleGuildMemberAdded _ (eventArgs : GuildMemberAddEventArgs) =
task {
let! exists = checkUserAlreadyInvited eventArgs.Member.Id
if not exists then
do! processNewUser eventArgs
} :> Task
let setCurrentWhitelistStock amount (ctx : IDiscordContext) =
task {
do! Messaging.defer ctx
let! result = DbService.setItemStock amount "WHITELIST"
if result then
do! Messaging.sendFollowUpMessage ctx $"Set Whitelist stock to {amount}"
else
do! Messaging.sendFollowUpMessage ctx $"Error setting WL to {amount}, make sure it's greater than 0"
} :> Task
type Inviter() =
inherit ApplicationCommandModule ()
[<SlashCommand("recruit", "Recruit another user to this discord and earn rewards")>]
member this.CreateInvite (ctx : InteractionContext) =
showInviteMessage (DiscordInteractionContext ctx) "RecruitCommand"
[<SlashCommand("recruited", "Get total invites from a specific user")>]
member this.ListInvitedPeople (ctx : InteractionContext) =
let ictx = DiscordInteractionContext ctx :> IDiscordContext
getInvitedUsersForId (ictx.GetDiscordMember()) ictx
// [<SlashCommand("invites-list", "List all the invites")>]
member this.ListServerInvites (ctx : InteractionContext) =
listServerInvites (DiscordInteractionContext ctx)
// [<SlashCommand("invites-clear", "Get total invites from a specific user")>]
member this.ClearInvites (ctx : InteractionContext) =
clearInvites (DiscordInteractionContext ctx)