627 lines
26 KiB
Forth
627 lines
26 KiB
Forth
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… You’re 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… You’re 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 = $"""
|
||
✅ You’re **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 you’ve 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 Brother’s 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 you’ve 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 you’ve 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)
|
||
|