Instrumentation
This commit is contained in:
parent
542adbf9a7
commit
694cab3e49
79
Bot/Analytics.fs
Normal file
79
Bot/Analytics.fs
Normal file
@ -0,0 +1,79 @@
|
||||
module Degenz.Analytics
|
||||
|
||||
open Mixpanel
|
||||
|
||||
let private mix = MixpanelClient(GuildEnvironment.tokenMixpanel)
|
||||
|
||||
let private track eventName id data =
|
||||
let map =
|
||||
[ "distinct_id" , box id
|
||||
"guild_id" , box GuildEnvironment.guildId
|
||||
"env" , box GuildEnvironment.environment ]
|
||||
mix.TrackAsync(eventName, data @ map |> dict) |> Async.AwaitTask |> Async.Ignore
|
||||
|
||||
let invitedUserEntered inviteCode inviterId inviteeId inviterName inviteeName =
|
||||
let data = [
|
||||
"user_display_name" , inviterName
|
||||
"invite_code" , inviteCode
|
||||
"invitee_id" , inviteeId
|
||||
"invitee_name" , inviteeName
|
||||
]
|
||||
track "Invited User Entered" inviterId data
|
||||
|
||||
let invitedUserAccepted inviteCode inviterId inviteeId inviterName inviteeName =
|
||||
let data = [
|
||||
"user_display_name" , inviterName
|
||||
"invite_code" , inviteCode
|
||||
"invitee_id" , inviteeId
|
||||
"invitee_name" , inviteeName
|
||||
]
|
||||
track "Invited User Accepted" inviterId data
|
||||
|
||||
let recruitCommand origin id name channelId channelName =
|
||||
let data = [
|
||||
"user_display_name" , name
|
||||
"origin" , origin
|
||||
"channel_id" , channelId
|
||||
"channel_name" , channelName
|
||||
]
|
||||
track "Recruit Command Invoked" id data
|
||||
|
||||
let recruitLinkButton inviteCode id name channelId channelName =
|
||||
let data = [
|
||||
"user_display_name" , name
|
||||
"invite_code" , inviteCode
|
||||
"channel_id" , channelId
|
||||
"channel_name" , channelName
|
||||
]
|
||||
track "Recruited Command Invoked" id data
|
||||
|
||||
let recruitedCommand totalUsers id name channelId channelName =
|
||||
let data = [
|
||||
"user_display_name" , name
|
||||
"total_users_at_the_time" , totalUsers
|
||||
"channel_id" , channelId
|
||||
"channel_name" , channelName
|
||||
]
|
||||
track "Recruited Command Invoked" id data
|
||||
|
||||
let whiteListButton availability id name =
|
||||
let data = [
|
||||
"user_display_name" , name
|
||||
"availability" , availability
|
||||
]
|
||||
track "Recruited Command Invoked" id data
|
||||
|
||||
let whiteListPurchased amount id name =
|
||||
let data = [
|
||||
"user_display_name" , name
|
||||
"purchase_amount" , amount
|
||||
]
|
||||
track "Recruited Command Invoked" id data
|
||||
|
||||
let trainingDojoStep step id name =
|
||||
let data = [
|
||||
"user_display_name" , name
|
||||
"step" , step
|
||||
]
|
||||
track "Recruited Command Invoked" id data
|
||||
|
@ -6,13 +6,10 @@ open DSharpPlus
|
||||
open DSharpPlus.SlashCommands
|
||||
open Degenz
|
||||
open Emzi0767.Utilities
|
||||
open Mixpanel
|
||||
//open Degenz.SlotMachine
|
||||
|
||||
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
|
||||
|
||||
let mix = MixpanelClient("SOMETOKEN")
|
||||
|
||||
let guild = GuildEnvironment.guildId
|
||||
|
||||
let hackerBattleConfig = DiscordConfiguration()
|
||||
|
@ -17,6 +17,7 @@
|
||||
<Compile Include="GameTypes.fs" />
|
||||
<Compile Include="GameHelpers.fs" />
|
||||
<Compile Include="DbService.fs" />
|
||||
<Compile Include="Analytics.fs" />
|
||||
<Compile Include="PlayerInteractions.fs" />
|
||||
<Compile Include="InviteTracker.fs" />
|
||||
<Compile Include="XP.fs" />
|
||||
|
@ -70,6 +70,7 @@ let handleTrainerStep1 (ctx : IDiscordContext) =
|
||||
.AsEphemeral(true)
|
||||
|
||||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
||||
do! Analytics.trainingDojoStep "LFG" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
} |> Async.StartAsTask :> Task
|
||||
|
||||
let defend (ctx : IDiscordContext) =
|
||||
@ -79,6 +80,7 @@ let defend (ctx : IDiscordContext) =
|
||||
let name = if System.String.IsNullOrEmpty m.Nickname then m.DisplayName else m.Nickname
|
||||
let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ Shield defaultShield ] ; Name = name } true
|
||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||
do! Analytics.trainingDojoStep "DefendCommand" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
} |> Async.StartAsTask :> Task
|
||||
|
||||
let handleDefenseMsg hackId = {
|
||||
@ -104,6 +106,7 @@ let handleDefense (ctx : IDiscordContext) =
|
||||
do! sendMessage' $"❌ HACKING FAILED!\n\n{playerName} defended hack from <@{Sensei.Id}>!"
|
||||
do! Async.Sleep 1500
|
||||
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Item.Name)
|
||||
do! Analytics.trainingDojoStep "ShieldActivated" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
} |> Async.StartAsTask :> Task
|
||||
|
||||
let handleTrainerStep3 (ctx : IDiscordContext) =
|
||||
@ -117,6 +120,7 @@ let handleTrainerStep3 (ctx : IDiscordContext) =
|
||||
+ $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHack.Item.Name}`")
|
||||
|
||||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
||||
do! Analytics.trainingDojoStep "LetsHack" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
} |> Async.StartAsTask :> Task
|
||||
|
||||
let hack (target : DiscordUser) (ctx : IDiscordContext) =
|
||||
@ -138,6 +142,7 @@ let hack (target : DiscordUser) (ctx : IDiscordContext) =
|
||||
.AsEphemeral(true)
|
||||
|
||||
do! ctx.FollowUp(builder) |> Async.AwaitTask
|
||||
do! Analytics.trainingDojoStep "HackCommand" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
} |> Async.StartAsTask :> Task
|
||||
|
||||
let handleHack (ctx : IDiscordContext) =
|
||||
@ -153,7 +158,8 @@ let handleHack (ctx : IDiscordContext) =
|
||||
+ "When you **HACK** other Degenz, you **TAKE** their 💰$GBT.\n"
|
||||
+ "But remember, hacks take time to recover, so use them wisely.")
|
||||
|
||||
do! Async.Sleep 2000
|
||||
do! Analytics.trainingDojoStep "HackActivated" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
do! Async.Sleep 1000
|
||||
|
||||
let message =
|
||||
$"""
|
||||
@ -181,6 +187,7 @@ type the `/arsenal` command NOW"""
|
||||
do! ctx.GetDiscordMember().GrantRoleAsync(role) |> Async.AwaitTask
|
||||
|
||||
do! sendFollowUpMessage ctx ($"Your training is now complete. If you want to buy more **HACKS & SHIELDS**, go to the <#{GuildEnvironment.channelArmory}> and type the `/buy-hack` and `/buy-shield` commands!")
|
||||
do! Analytics.trainingDojoStep "CompletedNoGifts" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
})
|
||||
|
||||
let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||||
@ -235,6 +242,7 @@ let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerActi
|
||||
do! Async.Sleep 1000
|
||||
|
||||
do! sendFollowUpMessage ctx $"Now get out of there and go hack other Degenz in the <#{GuildEnvironment.channelBattle}> channel!"
|
||||
do! Analytics.trainingDojoStep "CompletedWithGifts" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||||
else
|
||||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
|
||||
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask
|
||||
|
@ -15,11 +15,13 @@ let getId str = getVar str |> uint64
|
||||
let connectionString = (getVar "DATABASE_URL").Replace("postgresql://", "postgres://").Replace("?sslmode=require", "")
|
||||
|
||||
let guildId = getId "DISCORD_GUILD"
|
||||
let environment = getVar "ENVIRONMENT"
|
||||
let tokenPlayerInteractions = getVar "TOKEN_PLAYER_INTERACTIONS"
|
||||
let tokenSteal = getVar "TOKEN_STEAL"
|
||||
let tokenHackerBattle = getVar "TOKEN_HACKER_BATTLE"
|
||||
let tokenStore = getVar "TOKEN_STORE"
|
||||
let tokenInviter = getVar "TOKEN_INVITER"
|
||||
let tokenMixpanel = getVar "TOKEN_MIXPANEL"
|
||||
let channelEventsHackerBattle = getId "CHANNEL_EVENTS_HACKER_BATTLE"
|
||||
let channelTraining = getId "CHANNEL_TRAINING"
|
||||
let channelArmory = getId "CHANNEL_ARMORY"
|
||||
|
@ -199,7 +199,7 @@ let guildInviteEmbed =
|
||||
let button = DiscordButtonComponent(ButtonStyle.Success, $"CreateGuildInvite", $"GET MY UNIQUE LINK") :> DiscordComponent
|
||||
builder.AddComponents [| button |]
|
||||
|
||||
let private showInviteMessage (ctx : IDiscordContext) =
|
||||
let private showInviteMessage (ctx : IDiscordContext) origin =
|
||||
task {
|
||||
let builder = DiscordInteractionResponseBuilder().AsEphemeral(true)
|
||||
do! ctx.Respond(InteractionResponseType.DeferredChannelMessageWithSource, builder)
|
||||
@ -216,6 +216,7 @@ let private showInviteMessage (ctx : IDiscordContext) =
|
||||
You must **COMPLETE YOUR TRAINING FIRST!** Then you can `/recruit`...
|
||||
Go to <#{GuildEnvironment.channelTraining}> now to become a **HACKER**!
|
||||
"""
|
||||
do! Analytics.recruitCommand origin player.DiscordId (ctx.GetDiscordMember().Username) (ctx.GetChannel().Id) (ctx.GetChannel().Name)
|
||||
| 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
|
||||
@ -259,6 +260,9 @@ let private getInvitedUsersForId (ctx : IDiscordContext) = task {
|
||||
.AsEphemeral(true)
|
||||
.WithContent(str)
|
||||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
|
||||
let user = ctx.GetDiscordMember()
|
||||
let channel = ctx.GetChannel()
|
||||
do! Analytics.recruitedCommand total user.Id user.Username channel.Id channel.Name
|
||||
}
|
||||
|
||||
let clearInvites (ctx : IDiscordContext) = task {
|
||||
@ -281,6 +285,7 @@ let private processNewUser (eventArgs : GuildMemberAddEventArgs) =
|
||||
match result with
|
||||
| Some (_,count) ->
|
||||
if invite.Uses > count then
|
||||
do! Analytics.invitedUserEntered invite.Code invite.Inviter.Id eventArgs.Member.Id invite.Inviter.Username eventArgs.Member.Username
|
||||
do! addInvitedUser eventArgs.Member.Id invite.Code invite.Uses |> Async.Ignore
|
||||
| None -> ()
|
||||
} :> Task
|
||||
@ -291,36 +296,23 @@ let acceptInvite (ctx : IDiscordContext) (invitedPlayer : PlayerData) =
|
||||
| false ->
|
||||
let! _ = markInvitedAccepted invitedPlayer.DiscordId |> Async.Ignore
|
||||
try
|
||||
let! inviter = getInviteFromInvitedUser invitedPlayer.DiscordId
|
||||
let! player = DbService.tryFindPlayer inviter.Inviter
|
||||
let! invite = getInviteFromInvitedUser invitedPlayer.DiscordId
|
||||
let! player = DbService.tryFindPlayer invite.Inviter
|
||||
match player with
|
||||
| Some player ->
|
||||
do! DbService.updatePlayerCurrency (int InviteRewardAmount) player |> Async.Ignore
|
||||
let builder = DiscordMessageBuilder()
|
||||
builder.WithContent($"{invitedPlayer.Name} was recruited to the server. <@{player.DiscordId}> just earned {InviteRewardAmount} 💰$GBT for their efforts!") |> ignore
|
||||
builder.WithContent($"{invitedPlayer.Name} was recruited and is now a Degen. <@{player.DiscordId}> just earned {InviteRewardAmount} 💰$GBT for their efforts!") |> ignore
|
||||
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle)
|
||||
do! channel.SendMessageAsync(builder)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Ignore
|
||||
do! Analytics.invitedUserAccepted invite.Code player.DiscordId invitedPlayer.DiscordId player.Name invitedPlayer.Name
|
||||
| None -> return ()
|
||||
with _ -> ()
|
||||
| true -> return ()
|
||||
} :> Task
|
||||
|
||||
// If we do it like this then there's an obvious exploit where the user can come and go as many times and it will keep
|
||||
// rewarding GBT.
|
||||
//let handleGuildMemberRemoved _ (eventArgs : GuildMemberRemoveEventArgs) =
|
||||
// task {
|
||||
// do! removeInvitedUser eventArgs.Member.Id
|
||||
// } :> Task
|
||||
|
||||
//Degenz Game
|
||||
//Mint Date: April 2022
|
||||
//Supply: 3,333
|
||||
//Price: 1.984 $SOL
|
||||
|
||||
//Your NFT will be your In-Game Character that provides you with unique traits, and abilities in game.
|
||||
|
||||
let sendInitialEmbed (client : DiscordClient) =
|
||||
async {
|
||||
try
|
||||
@ -435,7 +427,8 @@ let handleGimmeWhitelist (ctx : IDiscordContext) =
|
||||
let recruitBtn = DiscordButtonComponent(ButtonStyle.Danger, $"ShowRecruitmentEmbed", $"Recruit Now") :> DiscordComponent
|
||||
|
||||
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
||||
match! tryGrantWhitelist ctx with
|
||||
let! availability = tryGrantWhitelist ctx
|
||||
match availability with
|
||||
| NotAHacker -> whitelistEmbed.Description <- notAHackerMsg
|
||||
| NotInGame -> whitelistEmbed.Description <- notInGameMsg
|
||||
| AlreadyWhitelisted ->
|
||||
@ -455,6 +448,13 @@ let handleGimmeWhitelist (ctx : IDiscordContext) =
|
||||
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 = $"""
|
||||
@ -476,10 +476,18 @@ let handleBuyWhitelist (ctx : IDiscordContext) =
|
||||
|
||||
let builder = DiscordFollowupMessageBuilder().AsEphemeral(true)
|
||||
match! tryGrantWhitelist ctx with
|
||||
| NotAHacker -> builder.Content <- $"You are somehow not a hacker anymore, what exactly are you doing?"
|
||||
| NotInGame -> builder.Content <- $"You somehow have left the game, what exactly are you doing?"
|
||||
| AlreadyWhitelisted -> builder.Content <- "🎉 You're already WHITELISTED!"
|
||||
| NotEnoughGBT _ -> builder.Content <- $"You somehow do not have enough $GBT, what exactly are you doing?"
|
||||
| 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)
|
||||
| Granted player ->
|
||||
let embed = DiscordEmbedBuilder()
|
||||
embed.Description <- buyWhitelistMsg
|
||||
@ -491,6 +499,7 @@ let handleBuyWhitelist (ctx : IDiscordContext) =
|
||||
do! ctx.GetDiscordMember().GrantRoleAsync(role)
|
||||
let! _ = DbService.updatePlayerCurrency -WhitelistPrice player
|
||||
builder.AddEmbed(embed) |> ignore
|
||||
do! ctx.FollowUp(builder)
|
||||
|
||||
// Send message to hall of privacy
|
||||
let builder = DiscordMessageBuilder()
|
||||
@ -499,8 +508,9 @@ let handleBuyWhitelist (ctx : IDiscordContext) =
|
||||
do! channel.SendMessageAsync(builder)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Ignore
|
||||
let user = ctx.GetDiscordMember()
|
||||
do! Analytics.whiteListPurchased WhitelistPrice user.Id user.Username
|
||||
|
||||
do! ctx.FollowUp(builder)
|
||||
} :> Task
|
||||
|
||||
let handleCreateInvite (ctx : IDiscordContext) =
|
||||
@ -532,6 +542,7 @@ let handleCreateInvite (ctx : IDiscordContext) =
|
||||
.AsEphemeral(true)
|
||||
|
||||
do! ctx.FollowUp(msg)
|
||||
do! Analytics.recruitLinkButton code user.Id user.Username (ctx.GetChannel().Id) (ctx.GetChannel().Name)
|
||||
} :> Task
|
||||
|
||||
let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
|
||||
@ -540,7 +551,7 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEv
|
||||
| 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
|
||||
| id when id.StartsWith("ShowRecruitmentEmbed") -> showInviteMessage eventCtx "RecruitButton"
|
||||
| _ ->
|
||||
task {
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
@ -561,7 +572,7 @@ type Inviter() =
|
||||
|
||||
[<SlashCommand("recruit", "Recruit another user to this discord and earn rewards")>]
|
||||
member this.CreateInvite (ctx : InteractionContext) =
|
||||
showInviteMessage (DiscordInteractionContext ctx)
|
||||
showInviteMessage (DiscordInteractionContext ctx) "RecruitCommand"
|
||||
|
||||
[<SlashCommand("recruited", "Get total invites from a specific user")>]
|
||||
member this.ListInvitedPeople (ctx : InteractionContext) =
|
||||
|
Loading…
x
Reference in New Issue
Block a user