Migrate cache and privacy to slash commands

This commit is contained in:
denverquane
2022-02-20 13:59:45 -05:00
parent e8ef20e087
commit 808311924f
9 changed files with 211 additions and 218 deletions

View File

@ -5,9 +5,9 @@ not for sale or redistribution.
Using AutoMuteUs in your Discord server (or as a general user) falls under "Legitimate Interest" in [Article 6(1)(f) of the
General Data Protection Regulation](https://eur-lex.europa.eu/legal-content/EN/TXT/?qid=1528874672298&uri=CELEX:02016R0679-20160504) (GDPR).
1. We use data collection to display and aggregate statistics about what games a Discord User has played in Among Us.
1. We use data collection to display and aggregate statistics about what games a Discord User has played in Among Us. (`/privacy showme`)
2. We only use the minimal amount of data/PII necessary to generate and process these statistics.
3. Users can opt-out of data collection at any time if they don't wish for AutoMuteUs to gather this data. (`.au priv optout`)
3. Users can opt-out of data collection at any time if they don't wish for AutoMuteUs to gather this data. (`/privacy optout`)
# What Data does AutoMuteUs collect?
AutoMuteUs collects a very small amount of user information for statistics. Your Discord UserID, and any in-game names you have used
@ -23,8 +23,8 @@ by AutoMuteUs is shown below:
```
AutoMuteUs uses a mapping of Discord UserIDs to arbitrary numerical IDs, which are used for correlating game events. If you
choose to delete the data that AutoMuteUs stores about you (with `.au privacy optout`), the mapping to your User ID is removed,
and the full history of your past games is deleted. Because of this, re-opting into data collection with AutoMuteUs (`.au privacy optin`) means
choose to delete the data that AutoMuteUs stores about you (with `/privacy optout`), the mapping to your User ID is removed,
and the full history of your past games is deleted. Because of this, re-opting into data collection with AutoMuteUs (`/privacy optin`) means
your past games and game events **are not recoverable**. Please carefully consider this before opting out, if you plan to
view your game statistics at any point in the future!

View File

@ -93,7 +93,7 @@ If you want to view command usage or see the available options, type `/help` in
| `/unlink` | @name | Manually unlink a player | `.au u @player` |
| `.au settings` | | View and change settings for the bot, such as the command prefix or mute behavior | |
| `.au pause` | None | Pause the bot, and don't let it automute anyone until unpaused. **will not un-mute muted players, be careful!** | |
| `.au privacy` | | View privacy and data collection information about the bot | |
| `/privacy` | | View privacy and data collection information about the bot | |
| `/info` | None | View general info about the Bot | |
| `.au map` | MAPNAME | View an image of an in-game map in the text channel. Two supported versions: simple or detailed(vent, camera, etc) | `.au map skeld detailed` |

View File

@ -22,6 +22,7 @@ var All = []*discordgo.ApplicationCommand{
&New,
&End,
&Refresh,
&Privacy,
}
func getCommand(cmd string) *discordgo.ApplicationCommand {

163
discord/command/privacy.go Normal file
View File

@ -0,0 +1,163 @@
package command
import (
"bytes"
"fmt"
"github.com/automuteus/utils/pkg/settings"
"github.com/automuteus/utils/pkg/storage"
"github.com/bwmarrin/discordgo"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type PrivacyStatus int
const (
PrivacyInfo PrivacyStatus = iota
PrivacyShowMe
PrivacyOptIn
PrivacyOptOut
PrivacyUnknown
PrivacyCacheClear
)
var Privacy = discordgo.ApplicationCommand{
Name: "privacy",
Description: "View AMU privacy info",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionString,
Name: "command",
Description: "Privacy command",
Required: false,
},
},
}
func GetPrivacyParam(options []*discordgo.ApplicationCommandInteractionDataOption) PrivacyStatus {
if len(options) == 0 {
return PrivacyInfo
}
opt := options[0].StringValue()
switch opt {
case "info":
fallthrough
case "help":
return PrivacyInfo
case "show":
fallthrough
case "me":
fallthrough
case "cache":
fallthrough
case "showme":
return PrivacyShowMe
case "optin":
return PrivacyOptIn
case "optout":
return PrivacyOptOut
case "clear":
fallthrough
case "clearcache":
fallthrough
case "cacheclear":
return PrivacyCacheClear
default:
return PrivacyUnknown
}
}
func PrivacyResponse(status PrivacyStatus, cached map[string]interface{}, user *storage.PostgresUser, err error, sett *settings.GuildSettings) *discordgo.InteractionResponse {
var content string
switch status {
case PrivacyUnknown:
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.unknownarg",
Other: "❌ Sorry, I didn't recognize that argument",
})
case PrivacyInfo:
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.info",
Other: "AutoMuteUs privacy and data collection details.\n" +
"More details [here](https://github.com/automuteus/automuteus/blob/master/PRIVACY.md)\n" +
"(I accept `showme`/`cache`,`optin`,`optout`, and `clearcache` as arguments)",
})
case PrivacyShowMe:
if cached == nil || len(cached) == 0 {
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.showme.nocache",
Other: "❌ I don't have any cached player names stored for you!",
})
} else {
buf := bytes.NewBuffer([]byte(sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.showme.cache",
Other: "❗ Here's your cached in-game names:",
})))
buf.WriteString("\n```\n")
for n := range cached {
buf.WriteString(fmt.Sprintf("%s\n", n))
}
buf.WriteString("```")
content = buf.String()
}
if user != nil && user.UserID != 0 {
content += "\n"
if user.Opt {
content += sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.showme.optin",
Other: "❗ You are opted **in** to data collection for game statistics",
})
} else {
content += sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.showme.optout",
Other: "❌ You are opted **out** of data collection for game statistics, or you haven't played a game yet",
})
}
}
case PrivacyOptOut:
fallthrough
case PrivacyOptIn:
if err == nil {
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.opt.success",
Other: "✅ I successfully changed your opt in/out status",
})
} else {
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.opt.error",
Other: "❌ I encountered an error changing your opt in/out status:\n`{{.Error}}`",
}, map[string]interface{}{
"Error": err.Error(),
})
}
case PrivacyCacheClear:
if err == nil {
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.cacheclear.success",
Other: "✅ I successfully cleared your player name cache",
})
} else {
content = sett.LocalizeMessage(&i18n.Message{
ID: "commands.privacy.cacheclear.error",
Other: "❌ I encountered an error clearing your player name cache:\n`{{.Error}}`",
}, map[string]interface{}{
"Error": err.Error(),
})
}
}
return &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: 1 << 6,
Content: content,
},
}
}

View File

@ -1,7 +1,6 @@
package discord
import (
"bytes"
"encoding/json"
"fmt"
"github.com/automuteus/automuteus/amongus"
@ -703,81 +702,29 @@ func commandFnMap(
}
func commandFnCache(
bot *Bot,
_ *Bot,
_ bool,
_ bool,
sett *settings.GuildSettings,
_ *discordgo.Guild,
message *discordgo.MessageCreate,
args []string,
cmd *Command,
m *discordgo.MessageCreate,
_ []string,
_ *Command,
) (string, interface{}) {
if len(args[1:]) == 0 {
return message.ChannelID, ConstructEmbedForCommand(*cmd, sett)
} else {
userID, err := discord.ExtractUserIDFromMention(args[1])
if err != nil {
log.Println(err)
return message.ChannelID, "I couldn't find a user by that name or ID!"
}
if len(args[2:]) == 0 {
cached := bot.RedisInterface.GetUsernameOrUserIDMappings(message.GuildID, userID)
if len(cached) == 0 {
return message.ChannelID, sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.Cache.emptyCachedNames",
Other: "I don't have any cached player names stored for that user!",
})
} else {
buf := bytes.NewBuffer([]byte(sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.Cache.cachedNames",
Other: "Cached in-game names:",
})))
buf.WriteString("\n```\n")
for n := range cached {
buf.WriteString(fmt.Sprintf("%s\n", n))
}
buf.WriteString("```")
return message.ChannelID, buf.String()
}
} else if strings.ToLower(args[2]) == clearArgumentString || strings.ToLower(args[2]) == "c" {
err := bot.RedisInterface.DeleteLinksByUserID(message.GuildID, userID)
if err != nil {
log.Println(err)
return message.ChannelID, err
} else {
return message.ChannelID, sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.Cache.Success",
Other: "Successfully deleted all cached names for that user!",
})
}
}
return "", nil
}
return m.ChannelID, replacedCommandResponse("/privacy cache", sett)
}
func commandFnPrivacy(
bot *Bot,
_ *Bot,
_ bool,
_ bool,
sett *settings.GuildSettings,
_ *discordgo.Guild,
message *discordgo.MessageCreate,
args []string,
cmd *Command,
m *discordgo.MessageCreate,
_ []string,
_ *Command,
) (string, interface{}) {
if message.Author != nil {
var arg = ""
if len(args[1:]) > 0 {
arg = args[1]
}
if arg == "" || (arg != "showme" && arg != "optin" && arg != "optout") {
return message.ChannelID, ConstructEmbedForCommand(*cmd, sett)
} else {
return message.ChannelID, bot.privacyResponse(message.GuildID, message.Author.ID, arg, sett)
}
}
return "", nil
return m.ChannelID, replacedCommandResponse("/privacy", sett)
}
func commandFnInfo(

View File

@ -688,130 +688,3 @@ func premiumInvitesEmbed(tier premium.Tier, sett *settings.GuildSettings) *disco
}
return &msg
}
func (bot *Bot) privacyResponse(guildID, authorID, arg string, sett *settings.GuildSettings) *discordgo.MessageEmbed {
desc := ""
switch arg {
case "showme":
cached := bot.RedisInterface.GetUsernameOrUserIDMappings(guildID, authorID)
if len(cached) == 0 {
desc = sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.ShowMe.emptyCachedNames",
Other: "❌ {{.User}} I don't have any cached player names stored for you!",
}, map[string]interface{}{
"User": "<@!" + authorID + ">",
})
} else {
buf := bytes.NewBuffer([]byte(sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.ShowMe.cachedNames",
Other: "❗ {{.User}} Here's your cached in-game names:",
}, map[string]interface{}{
"User": "<@!" + authorID + ">",
})))
buf.WriteString("\n```\n")
for n := range cached {
buf.WriteString(fmt.Sprintf("%s\n", n))
}
buf.WriteString("```")
desc = buf.String()
}
desc += "\n"
user, _ := bot.PostgresInterface.GetUserByString(authorID)
if user != nil && user.Opt && user.UserID != 0 {
desc += sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.ShowMe.linkedID",
Other: "❗ {{.User}} You are opted **in** to data collection for game statistics",
}, map[string]interface{}{
"User": "<@!" + authorID + ">",
})
} else {
desc += sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.ShowMe.unlinkedID",
Other: "❌ {{.User}} You are opted **out** of data collection for game statistics, or you haven't played a game yet",
}, map[string]interface{}{
"User": "<@!" + authorID + ">",
})
}
case "optout":
err := bot.RedisInterface.DeleteLinksByUserID(guildID, authorID)
if err != nil {
log.Println(err)
} else {
desc = sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.ForgetMe.Success",
Other: "✅ {{.User}} I successfully deleted your cached player names",
},
map[string]interface{}{
"User": "<@!" + authorID + ">",
})
desc += "\n"
didOpt, err := bot.PostgresInterface.OptUserByString(authorID, false)
if err != nil {
log.Println(err)
} else {
if didOpt {
desc += sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.optout.SuccessDB",
Other: "✅ {{.User}} I successfully opted you out of data collection",
},
map[string]interface{}{
"User": "<@!" + authorID + ">",
})
} else {
desc += sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.optout.FailDB",
Other: "❌ {{.User}} You are already opted out of data collection",
},
map[string]interface{}{
"User": "<@!" + authorID + ">",
})
}
}
}
case "optin":
didOpt, err := bot.PostgresInterface.OptUserByString(authorID, true)
if err != nil {
log.Println(err)
} else {
if didOpt {
desc += sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.optin.SuccessDB",
Other: "✅ {{.User}} I successfully opted you into data collection",
},
map[string]interface{}{
"User": "<@!" + authorID + ">",
})
} else {
desc += sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.optin.FailDB",
Other: "❌ {{.User}} You are already opted into data collection",
},
map[string]interface{}{
"User": "<@!" + authorID + ">",
})
}
}
}
msg := discordgo.MessageEmbed{
URL: "",
Type: "",
Title: sett.LocalizeMessage(&i18n.Message{
ID: "responses.privacyResponse.Title",
Other: "AutoMuteUs Privacy",
}),
Description: desc,
Timestamp: time.Now().Format(ISO8601),
Color: 10181046, // PURPLE
Footer: nil,
Image: nil,
Thumbnail: nil,
Video: nil,
Provider: nil,
Author: nil,
Fields: nil,
}
return &msg
}

View File

@ -140,6 +140,37 @@ func (bot *Bot) handleInteractionCreate(s *discordgo.Session, i *discordgo.Inter
bot.RefreshGameStateMessage(gsr, sett)
// TODO inform the user of how successful this command was
response = command.RefreshResponse(sett)
case "privacy":
privArg := command.GetPrivacyParam(i.ApplicationCommandData().Options)
switch privArg {
case command.PrivacyUnknown:
fallthrough
case command.PrivacyInfo:
response = command.PrivacyResponse(privArg, nil, nil, nil, sett)
case command.PrivacyOptOut:
err := bot.RedisInterface.DeleteLinksByUserID(i.GuildID, i.Member.User.ID)
if err != nil {
// we send the cache clear type here because that's exactly what we failed to do; clear the cache
response = command.PrivacyResponse(command.PrivacyCacheClear, nil, nil, err, sett)
// don't fall-through; exit here
break
}
fallthrough
case command.PrivacyOptIn:
_, err := bot.PostgresInterface.OptUserByString(i.Member.User.ID, privArg == command.PrivacyOptIn)
response = command.PrivacyResponse(privArg, nil, nil, err, sett)
case command.PrivacyShowMe:
cached := bot.RedisInterface.GetUsernameOrUserIDMappings(i.GuildID, i.Member.User.ID)
user, err := bot.PostgresInterface.GetUserByString(i.Member.User.ID)
response = command.PrivacyResponse(privArg, cached, user, err, sett)
case command.PrivacyCacheClear:
err := bot.RedisInterface.DeleteLinksByUserID(i.GuildID, i.Member.User.ID)
response = command.PrivacyResponse(privArg, nil, nil, err, sett)
}
}
if response != nil {
err := s.InteractionRespond(i.Interaction, response)

1
go.mod
View File

@ -7,7 +7,6 @@ require (
github.com/automuteus/utils v0.0.27
github.com/bsm/redislock v0.7.1
github.com/bwmarrin/discordgo v0.23.3-0.20220216202327-6015eed9333e
github.com/georgysavva/scany v0.2.7 // indirect
github.com/go-redis/redis/v8 v8.8.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0 // indirect

23
go.sum
View File

@ -25,20 +25,6 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/automuteus/galactus v1.2.2 h1:8OMVzXutsHO+R1D1lW6qxzmZrAwNyTADw503B1ARGgg=
github.com/automuteus/galactus v1.2.2/go.mod h1:fWFnpCRyOs9wSVHvksupPlMPiAEO6CGhbI+uPtrmJcs=
github.com/automuteus/utils v0.0.20 h1:bpGojncojhJ230lZsuYQzcMb50Pf/73SWeHWGvmDpCY=
github.com/automuteus/utils v0.0.20/go.mod h1:/xi20VOLnhfqBM6AcGDKa4E8fw70dm/IcuM+0944diE=
github.com/automuteus/utils v0.0.21 h1:ksN/4S8psvLuTD62+PVKuDNm+MKqO98pNs5sOtX6XN0=
github.com/automuteus/utils v0.0.21/go.mod h1:/xi20VOLnhfqBM6AcGDKa4E8fw70dm/IcuM+0944diE=
github.com/automuteus/utils v0.0.22 h1:Jc9yO5WH1hoP3dNFmOYb0Cf6M0Doyc4G77MDCpHb4Go=
github.com/automuteus/utils v0.0.22/go.mod h1:ul0yR8WaX9B5y3ATKcvnnXtBSqLTR3WycwRkUP7bQdY=
github.com/automuteus/utils v0.0.23 h1:Hfa2Oy5oR+LJm3tZDIlakb47Np6QChlG8tvlXx1WULA=
github.com/automuteus/utils v0.0.23/go.mod h1:ul0yR8WaX9B5y3ATKcvnnXtBSqLTR3WycwRkUP7bQdY=
github.com/automuteus/utils v0.0.24 h1:bQ0yTO1EyG9MU4U8Y9j5TPr5nTIEGItOmLCEBk6UarU=
github.com/automuteus/utils v0.0.24/go.mod h1:M1m7GUOxiusOuLKLXv4c3Rnb0BSsZpA+fqmezCg9QTg=
github.com/automuteus/utils v0.0.25 h1:fvLr8DAkgs6NHFQni8BFDxNliPOyjF+93ZTv3g5MdLQ=
github.com/automuteus/utils v0.0.25/go.mod h1:M1m7GUOxiusOuLKLXv4c3Rnb0BSsZpA+fqmezCg9QTg=
github.com/automuteus/utils v0.0.26 h1:DN9ZlMes1NtBxDBA9pDjnofyznOwQZ/EH6ByWMvZ2NI=
github.com/automuteus/utils v0.0.26/go.mod h1:M1m7GUOxiusOuLKLXv4c3Rnb0BSsZpA+fqmezCg9QTg=
github.com/automuteus/utils v0.0.27 h1:j7ccmH6AwSf6A0N54qneTwvlybcMA8KMF6HDoVFlVR8=
github.com/automuteus/utils v0.0.27/go.mod h1:M1m7GUOxiusOuLKLXv4c3Rnb0BSsZpA+fqmezCg9QTg=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
@ -56,8 +42,6 @@ github.com/bsm/gomega v1.13.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATs
github.com/bsm/redislock v0.7.1 h1:nBMm91MRuGOOSlHZNEF0+HpiaH1i8QpSALrF/q7b/Es=
github.com/bsm/redislock v0.7.1/go.mod h1:TSF3xUotaocycoHjVAp535/bET+ZmvrtcyNrXc0Whm8=
github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/bwmarrin/discordgo v0.23.3-0.20220216202327-6015eed9333e h1:P7ba3v4TFTuN7eSJiegQBTWJVB560/nz5VkLUOLRuhI=
github.com/bwmarrin/discordgo v0.23.3-0.20220216202327-6015eed9333e/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
@ -84,7 +68,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverquane/amongusdiscord v1.4.2 h1:1ZFMNQ+UV30ANr/1iMnK72FPOOW7wUnp8QKB3k4k/Cc=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@ -172,7 +155,6 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -503,9 +485,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -545,8 +525,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -592,7 +572,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=