Setup inviter bot. Fix bug with transaction

This commit is contained in:
Joseph Ferano 2022-03-03 22:46:51 +07:00
parent 24f56c046c
commit 6b2b9a86f3
4 changed files with 170 additions and 139 deletions

View File

@ -4,9 +4,6 @@ open System.Threading.Tasks
open DSharpPlus open DSharpPlus
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open Degenz open Degenz
open Degenz.HackerBattle
open Degenz.Store
open Degenz.Thief
open Emzi0767.Utilities open Emzi0767.Utilities
//open Degenz.SlotMachine //open Degenz.SlotMachine
@ -17,13 +14,11 @@ let guild = GuildEnvironment.guildId
let hackerBattleConfig = DiscordConfiguration() let hackerBattleConfig = DiscordConfiguration()
let storeConfig = DiscordConfiguration() let storeConfig = DiscordConfiguration()
let stealConfig = DiscordConfiguration() let stealConfig = DiscordConfiguration()
let inviterConfig = DiscordConfiguration()
//let slotMachineConfig = DiscordConfiguration() //let slotMachineConfig = DiscordConfiguration()
//hackerBattleConfig.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace //hackerBattleConfig.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
//storeConfig.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace //storeConfig.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
//let configs = [| hackerBattleConfig ; storeConfig ; slotMachineConfig ; |]
let configs = [ hackerBattleConfig ; storeConfig ; stealConfig ]
hackerBattleConfig.TokenType <- TokenType.Bot hackerBattleConfig.TokenType <- TokenType.Bot
hackerBattleConfig.Intents <- DiscordIntents.All hackerBattleConfig.Intents <- DiscordIntents.All
@ -33,39 +28,40 @@ storeConfig.Intents <- DiscordIntents.All
stealConfig.TokenType <- TokenType.Bot stealConfig.TokenType <- TokenType.Bot
stealConfig.Intents <- DiscordIntents.All stealConfig.Intents <- DiscordIntents.All
inviterConfig.TokenType <- TokenType.Bot
inviterConfig.Intents <- DiscordIntents.All
hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle
storeConfig.Token <- GuildEnvironment.tokenStore storeConfig.Token <- GuildEnvironment.tokenStore
stealConfig.Token <- GuildEnvironment.tokenSteal stealConfig.Token <- GuildEnvironment.tokenSteal
inviterConfig.Token <- GuildEnvironment.tokenInviter
//slotMachineConfig.Token <- Environment.GetEnvironmentVariable("BOT_SLOT_MACHINE") //slotMachineConfig.Token <- Environment.GetEnvironmentVariable("BOT_SLOT_MACHINE")
let hackerBattleBot = new DiscordClient(hackerBattleConfig) let hackerBattleBot = new DiscordClient(hackerBattleConfig)
let storeBot = new DiscordClient(storeConfig) let storeBot = new DiscordClient(storeConfig)
let stealBot = new DiscordClient(stealConfig) let stealBot = new DiscordClient(stealConfig)
let inviterBot = new DiscordClient(inviterConfig)
//let slotMachineBot = new DiscordClient(slotMachineConfig) //let slotMachineBot = new DiscordClient(slotMachineConfig)
//let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |] //let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |]
let hackerCommands = hackerBattleBot.UseSlashCommands() let hackerCommands = hackerBattleBot.UseSlashCommands()
let storeCommands = storeBot.UseSlashCommands() let storeCommands = storeBot.UseSlashCommands()
let stealCommands = stealBot.UseSlashCommands() let stealCommands = stealBot.UseSlashCommands()
let inviterCommands = inviterBot.UseSlashCommands()
//let sc3 = slotMachineBot.UseSlashCommands() //let sc3 = slotMachineBot.UseSlashCommands()
hackerCommands.RegisterCommands<HackerGame>(guild); hackerCommands.RegisterCommands<HackerBattle.HackerGame>(guild);
storeCommands.RegisterCommands<Store>(guild); storeCommands.RegisterCommands<Store.Store>(guild);
stealCommands.RegisterCommands<StealGame>(guild); stealCommands.RegisterCommands<Thief.StealGame>(guild);
inviterCommands.RegisterCommands<InviteTracker.Inviter>(guild);
//hackerCommands.RegisterCommands<RPSGame>(guild); //hackerCommands.RegisterCommands<RPSGame>(guild);
//sc3.RegisterCommands<SlotMachine>(guild); //sc3.RegisterCommands<SlotMachine>(guild);
hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent)) hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))
storeBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleStoreEvents)) storeBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleStoreEvents))
stealBot.add_ComponentInteractionCreated(AsyncEventHandler(Thief.handleStealButton)) stealBot.add_ComponentInteractionCreated(AsyncEventHandler(Thief.handleStealButton))
hackerBattleBot.add_GuildMemberAdded(AsyncEventHandler(fun client ea -> inviterBot.add_GuildMemberAdded(AsyncEventHandler(InviteTracker.handleGuildMemberAdded))
task { inviterBot.add_GuildMemberRemoved(AsyncEventHandler(InviteTracker.handleGuildMemberRemoved))
let! guildInvites = ea.Guild.GetInvitesAsync()
let! cachedInvites = InviteTracker.getInvites()
for invite in guildInvites do
if invite.Uses < (snd cachedInvites.[invite.Code]) then
do! InviteTracker.addInvitedUser ea.Member.Id invite.Code |> Async.Ignore
}))
let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) = let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) =
async { async {
@ -94,6 +90,8 @@ GuildEnvironment.botUserArmory <- Some storeBot.CurrentUser
stealBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously stealBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
inviterBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
//let channel = hackerBattleBot.GetChannelAsync(1234uL) |> Async.AwaitTask |> Async.RunSynchronously //let channel = hackerBattleBot.GetChannelAsync(1234uL) |> Async.AwaitTask |> Async.RunSynchronously
//channel.invi //channel.invi

View File

@ -243,93 +243,6 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
do! eventCtx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask do! eventCtx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
} }
let createInvite (ctx : IDiscordContext) =
task {
let channel = ctx.GetGuild().Channels.[GuildEnvironment.channelWelcome]
let! invite = channel.CreateInviteAsync(max_age = 259200)
do! InviteTracker.createInvite (ctx.GetDiscordMember().Id) invite.Code |> Async.Ignore
let embed =
DiscordEmbedBuilder()
.WithDescription($"Send this invite to your friend, when they join, they can type the `/enter-code` slash command\n\n
```https://discord.gg/{invite.Code}```")
.WithImageUrl("https://pbs.twimg.com/profile_banners/1449270642340089856/1640071520/1500x500")
.WithTitle("Invite Code")
let msg =
DiscordInteractionResponseBuilder()
.AddEmbed(embed)
.AsEphemeral(true)
.WithContent($"https://discord.gg/{invite.Code}")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let 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 (ctx : IDiscordContext) userId = task {
let! total = InviteTracker.getInviteAttributions(userId)
let msg =
DiscordInteractionResponseBuilder()
.AsEphemeral(true)
.WithContent($"<@{userId}> has invited {total} people")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let getInvitedUsers (ctx : IDiscordContext) userId = task {
let! users = InviteTracker.getInvitedUsers(userId)
let sb = StringBuilder()
for user in users do
sb.AppendLine($"<@{user}>") |> ignore
let msg =
DiscordInteractionResponseBuilder()
.AsEphemeral(true)
.WithContent($"<@{userId}> has invited the following people:\n{sb}")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let clearInvites (ctx : IDiscordContext) = task {
let! invites = ctx.GetGuild().GetInvitesAsync()
do!
invites
|> Seq.map (fun invite -> invite.DeleteAsync() |> Async.AwaitTask)
|> Async.Parallel
|> Async.Ignore
}
//let invite (ctx : IDiscordContext) =
// task {
// let code = Guid.NewGuid().ToString().Substring(0, 7)
//
//// let embed1 =
//// DiscordEmbedBuilder()
//// .WithImageUrl("https://pbs.twimg.com/profile_banners/1449270642340089856/1640071520/1500x500")
// let embed2 =
// DiscordEmbedBuilder()
// .WithDescription($"Send this invite to your friend, when they join, type the `/enter-code` slash command\n\n```{code}```")
// .WithImageUrl("https://pbs.twimg.com/profile_banners/1449270642340089856/1640071520/1500x500")
// .WithTitle("Invite Code")
//
// let msg =
// DiscordInteractionResponseBuilder()
// .AsEphemeral(true)
//// .AddEmbed(embed1)
// .AddEmbed(embed2)
//
// do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
// }
type HackerGame() = type HackerGame() =
inherit ApplicationCommandModule () inherit ApplicationCommandModule ()
@ -353,26 +266,6 @@ type HackerGame() =
do! Messaging.sendSimpleResponse ctx msg do! Messaging.sendSimpleResponse ctx msg
} }
[<SlashCommand("invite-create", "Invite user to this discord and earn rewards")>]
member this.CreateInvite (ctx : InteractionContext) =
createInvite (DiscordInteractionContext ctx)
[<SlashCommand("invites-list", "List all the invites")>]
member this.ListServerInvites (ctx : InteractionContext) =
listServerInvites (DiscordInteractionContext ctx)
[<SlashCommand("invites-attributions", "Get total invites from a specific user")>]
member this.getAttributions (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
getAttributions (DiscordInteractionContext ctx) user.Id
[<SlashCommand("invited-people", "Get total invites from a specific user")>]
member this.ListInvitedPeople (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
getInvitedUsers (DiscordInteractionContext ctx) user.Id
[<SlashCommand("invites-clear", "Get total invites from a specific user")>]
member this.ClearInvites (ctx : InteractionContext) =
clearInvites (DiscordInteractionContext ctx)
[<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>] [<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
member this.Arsenal (ctx : InteractionContext) = member this.Arsenal (ctx : InteractionContext) =
enforceChannels (DiscordInteractionContext ctx) (Trainer.handleArsenal) arsenal enforceChannels (DiscordInteractionContext ctx) (Trainer.handleArsenal) arsenal

View File

@ -18,6 +18,7 @@ let tokenPlayerInteractions = getVar "TOKEN_PLAYER_INTERACTIONS"
let tokenSteal = getVar "TOKEN_STEAL" let tokenSteal = getVar "TOKEN_STEAL"
let tokenHackerBattle = getVar "TOKEN_HACKER_BATTLE" let tokenHackerBattle = getVar "TOKEN_HACKER_BATTLE"
let tokenStore = getVar "TOKEN_STORE" let tokenStore = getVar "TOKEN_STORE"
let tokenInviter = getVar "TOKEN_INVITER"
let channelEventsHackerBattle = getId "CHANNEL_EVENTS_HACKER_BATTLE" let channelEventsHackerBattle = getId "CHANNEL_EVENTS_HACKER_BATTLE"
let channelTraining = getId "CHANNEL_TRAINING" let channelTraining = getId "CHANNEL_TRAINING"
let channelArmory = getId "CHANNEL_ARMORY" let channelArmory = getId "CHANNEL_ARMORY"
@ -30,6 +31,7 @@ let channelWelcome = getId "CHANNEL_WELCOME"
//let channelThievery = getId "CHANNEL_THIEVERY" //let channelThievery = getId "CHANNEL_THIEVERY"
let botIdHackerBattle = getId "BOT_HACKER_BATTLE" let botIdHackerBattle = getId "BOT_HACKER_BATTLE"
let botIdArmory = getId "BOT_ARMORY" let botIdArmory = getId "BOT_ARMORY"
let botInviter = getId "BOT_INVITER"
let roleTrainee = getId "ROLE_TRAINEE" let roleTrainee = getId "ROLE_TRAINEE"
let rolePrisoner = getId "ROLE_PRISONER" let rolePrisoner = getId "ROLE_PRISONER"

View File

@ -1,7 +1,13 @@
module Degenz.InviteTracker module Degenz.InviteTracker
open System open System.Text
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Messaging
open Npgsql.FSharp open Npgsql.FSharp
let connStr = GuildEnvironment.connectionString let connStr = GuildEnvironment.connectionString
@ -18,7 +24,7 @@ let getInvites () = async {
|> Sql.connect |> Sql.connect
|> Sql.query """ |> Sql.query """
SELECT code, inviter, count FROM invite SELECT code, inviter, count FROM invite
WHERE created_at > (current_timestamp at time zone 'utc') - interval '3 day' WHERE created_at > (current_timestamp at time zone 'utc') - interval '1 day'
""" """
|> Sql.executeAsync (fun read -> { |> Sql.executeAsync (fun read -> {
Code = read.string "code" Code = read.string "code"
@ -33,34 +39,50 @@ let getInvites () = async {
} }
let createInvite inviter code = let createInvite inviter code =
connStr connStr
|> Sql.connect |> Sql.connect
|> Sql.parameters [ "code" , Sql.string code ; "inviter" , Sql.string (string inviter) ] |> Sql.parameters [ "code" , Sql.string code ; "inviter" , Sql.string (string inviter) ]
|> Sql.query "INSERT INTO invite (code, inviter) VALUES (@code, @inviter)" |> Sql.query "INSERT INTO invite (code, inviter) VALUES (@code, @inviter)"
|> Sql.executeNonQueryAsync |> Sql.executeNonQueryAsync
|> Async.AwaitTask |> Async.AwaitTask
|> Async.Ignore
let addInvitedUser did code = let addInvitedUser did code count =
try try
connStr connStr
|> Sql.connect |> Sql.connect
|> Sql.executeTransactionAsync [ |> Sql.executeTransactionAsync [
""" """
WITH invite AS (SELECT id FROM invite WHERE code = @code) INSERT INTO invited_user (discord_id, invite_id)
INSERT INTO invited_user (discord_id, invite_id) SELECT @discord_id, invite.id FROM invite; VALUES (@did, (SELECT id FROM invite WHERE code = @code));
""" , [ [ "@discord_id" , Sql.string (string did) ] ; [ "@code" , Sql.string code ] ] """ , [ [ "@code" , Sql.string code ; "@did" , Sql.string (string did) ] ]
"UPDATE invite SET count = count + 1 WHERE code = @code" , [ [ "@code" , Sql.string code ] ] "UPDATE invite SET count = @count WHERE code = @code" , [ [ "count" , Sql.int count ; "code" , Sql.string code ] ]
] ]
|> Async.AwaitTask |> Async.AwaitTask
|> Async.Ignore |> Async.Ignore
with _ -> async.Zero () with _ -> async.Zero ()
let 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 getInviteAttributions userId = let getInviteAttributions userId =
connStr connStr
|> Sql.connect |> Sql.connect
|> Sql.parameters [ "did" , Sql.string (string userId) ] |> Sql.parameters [ "did" , Sql.string (string userId) ]
|> Sql.query "SELECT sum(count) AS total FROM invite WHERE inviter = @did" |> Sql.query """
|> Sql.executeRowAsync (fun read -> read.int "total") SELECT count(*) FROM invited_user
JOIN invite ON invite.id = invited_user.invite_id
WHERE invite.inviter = @did
"""
|> Sql.executeRowAsync (fun read -> read.int "count")
|> Async.AwaitTask |> Async.AwaitTask
let getInvitedUsers userId = let getInvitedUsers userId =
@ -73,3 +95,119 @@ let getInvitedUsers userId =
""" """
|> Sql.executeAsync (fun read -> read.string "discord_id" |> uint64) |> Sql.executeAsync (fun read -> read.string "discord_id" |> uint64)
|> Async.AwaitTask |> Async.AwaitTask
let createGuildInvite (ctx : IDiscordContext) =
task {
let channel = ctx.GetGuild().Channels.[GuildEnvironment.channelWelcome]
let! invite = channel.CreateInviteAsync(max_age = 86400)
// When a player generates an invite code but it hasn't expired, it generates the same code, creating a duplicate entry
// so catch the exception thrown because the code column is unique
try
do! createInvite (ctx.GetDiscordMember().Id) invite.Code
with _ -> ()
let embed =
DiscordEmbedBuilder()
.WithDescription($"Use this invite link to earn invite points for future rewards.\nExpires in 1 day.
```https://discord.gg/{invite.Code}```")
.WithImageUrl("https://pbs.twimg.com/profile_banners/1449270642340089856/1640071520/1500x500")
.WithTitle("Invite Link")
let msg =
DiscordInteractionResponseBuilder()
.AddEmbed(embed)
.AsEphemeral(true)
.WithContent($"https://discord.gg/{invite.Code}")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let 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 (ctx : IDiscordContext) userId = task {
let! total = getInviteAttributions(userId)
let msg =
DiscordInteractionResponseBuilder()
.AsEphemeral(true)
.WithContent($"<@{userId}> has invited {total} people")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let getInvitedUsersForId (ctx : IDiscordContext) userId = task {
let! users = getInvitedUsers(userId)
let sb = StringBuilder()
for user in users do
sb.AppendLine($"<@{user}>") |> ignore
let msg =
DiscordInteractionResponseBuilder()
.AsEphemeral(true)
.WithContent($"<@{userId}> has invited the following people:\n{sb}")
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg)
}
let clearInvites (ctx : IDiscordContext) = task {
let! invites = ctx.GetGuild().GetInvitesAsync()
do!
invites
|> Seq.map (fun invite -> invite.DeleteAsync() |> Async.AwaitTask)
|> Async.Parallel
|> Async.Ignore
}
let handleGuildMemberAdded _ (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
| None -> ()
} :> Task
let handleGuildMemberRemoved _ (eventArgs : GuildMemberRemoveEventArgs) =
task {
// let! guildInvites = eventArgs.Guild.GetInvitesAsync()
// let! cachedInvites = getInvites()
// for invite in guildInvites do
// if invite.Uses < (snd cachedInvites.[invite.Code]) then
// do! addInvitedUser eventArgs.Member.Id invite.Code |> Async.Ignore
return ()
} :> Task
type Inviter() =
inherit ApplicationCommandModule ()
[<SlashCommand("invite-create", "Invite user to this discord and earn rewards")>]
member this.CreateInvite (ctx : InteractionContext) =
createGuildInvite (DiscordInteractionContext ctx)
[<SlashCommand("invites-list", "List all the invites")>]
member this.ListServerInvites (ctx : InteractionContext) =
listServerInvites (DiscordInteractionContext ctx)
[<SlashCommand("invites-attributions", "Get total invites from a specific user")>]
member this.getAttributions (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
getAttributions (DiscordInteractionContext ctx) user.Id
[<SlashCommand("invited-people", "Get total invites from a specific user")>]
member this.ListInvitedPeople (ctx : InteractionContext, [<Option("player", "The player you want to check")>] user : DiscordUser) =
getInvitedUsersForId (DiscordInteractionContext ctx) user.Id
[<SlashCommand("invites-clear", "Get total invites from a specific user")>]
member this.ClearInvites (ctx : InteractionContext) =
clearInvites (DiscordInteractionContext ctx)