Compare commits

..

2 commits

Author SHA1 Message Date
0d6a1398f0 history repeats itself
discordgo -> arikawa
2023-07-26 20:27:04 -06:00
7af5462d54 large guild fixes 2023-07-26 16:51:25 -06:00
19 changed files with 802 additions and 367 deletions

View file

@ -2,10 +2,8 @@ package commands
import ( import (
"fmt" "fmt"
"github.com/bwmarrin/discordgo"
) )
func ClearCommand(session *discordgo.Session) { func ClearCommand() {
fmt.Print("\n\r\033[H\033[2J") fmt.Print("\n\r\033[H\033[2J")
} }

View file

@ -5,10 +5,10 @@ import (
"github.com/Cynosphere/comcord/lib" "github.com/Cynosphere/comcord/lib"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
) )
func EmoteCommand(session *discordgo.Session) { func EmoteCommand() {
channelId := state.GetCurrentChannel() channelId := state.GetCurrentChannel()
if channelId == "" { if channelId == "" {
fmt.Print("<not in a channel>\n\r") fmt.Print("<not in a channel>\n\r")
@ -16,7 +16,7 @@ func EmoteCommand(session *discordgo.Session) {
} }
prompt := ":emote> " prompt := ":emote> "
lib.MakePrompt(session, prompt, true, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(prompt, true, func(input string, interrupt bool) {
if input == "" { if input == "" {
if interrupt { if interrupt {
fmt.Print("^C<no message sent>\n\r") fmt.Print("^C<no message sent>\n\r")
@ -25,10 +25,18 @@ func EmoteCommand(session *discordgo.Session) {
} }
} else { } else {
fmt.Print(prompt, input, "\n\r") fmt.Print(prompt, input, "\n\r")
_, err := session.ChannelMessageSend(channelId, "*" + input + "*") client := state.GetClient()
snowflake, err := discord.ParseSnowflake(channelId)
if err != nil {
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
return
}
_, err = client.SendMessage(discord.ChannelID(snowflake), "*" + input + "*")
if err != nil { if err != nil {
fmt.Print("<failed to send message: ", err, ">\n\r") fmt.Print("<failed to send message: ", err.Error(), ">\n\r")
} }
// TODO: update afk state // TODO: update afk state

View file

@ -10,9 +10,9 @@ import (
"github.com/Cynosphere/comcord/lib" "github.com/Cynosphere/comcord/lib"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/mgutz/ansi"
tsize "github.com/kopoli/go-terminal-size" tsize "github.com/kopoli/go-terminal-size"
"github.com/mgutz/ansi"
) )
var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`) var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
@ -23,31 +23,38 @@ type GuildListing struct {
Online int Online int
} }
func ListGuildsCommand(session *discordgo.Session) { func ListGuildsCommand() {
client := state.GetClient()
longest := 0 longest := 0
guilds := make([]GuildListing, 0) guilds := make([]GuildListing, 0)
for _, guild := range session.State.Guilds { clientGuilds, err := client.Guilds()
if err != nil {
fmt.Print("<failed to get guilds: ", err.Error(), ">\n\r")
return
}
for _, guild := range clientGuilds {
length := utf8.RuneCountInString(guild.Name) length := utf8.RuneCountInString(guild.Name)
if length > longest { if length > longest {
longest = length longest = length
} }
guildWithCounts, err := session.GuildWithCounts(guild.ID) withCount, err := client.GuildWithCount(guild.ID)
if err != nil { if err == nil {
guilds = append(guilds, GuildListing{ guilds = append(guilds, GuildListing{
Name: guild.Name, Name: guild.Name,
Members: guild.MemberCount, Members: int(withCount.ApproximateMembers),
Online: 0, Online: int(withCount.ApproximatePresences),
})
} else {
guilds = append(guilds, GuildListing{
Name: guild.Name,
Members: int(guild.ApproximateMembers),
Online: int(guild.ApproximatePresences),
}) })
return
} }
guilds = append(guilds, GuildListing{
Name: guild.Name,
Members: guildWithCounts.ApproximateMemberCount,
Online: guildWithCounts.ApproximatePresenceCount,
})
} }
fmt.Print("\n\r") fmt.Print("\n\r")
@ -60,64 +67,95 @@ func ListGuildsCommand(session *discordgo.Session) {
fmt.Print("\n\r") fmt.Print("\n\r")
} }
func GetSortedChannels(session *discordgo.Session, guildId string, withCategories bool, withPrivate bool) []*discordgo.Channel { func GetSortedChannels(guildId string, withCategories bool, withPrivate bool) []discord.Channel {
channels := make([]*discordgo.Channel, 0) client := state.GetClient()
guild, err := session.State.Guild(guildId) channels := make([]discord.Channel, 0)
guildSnowflake, err := discord.ParseSnowflake(guildId)
if err != nil {
return channels
}
parsedGuildId := discord.GuildID(guildSnowflake)
guild, err := client.GuildStore.Guild(parsedGuildId)
if err != nil {
return channels
}
guildChannels, err := client.ChannelStore.Channels(guild.ID)
if err != nil {
return channels
}
self, err := client.MeStore.Me()
if err != nil {
return channels
}
selfMember, err := client.MemberStore.Member(guild.ID, self.ID)
if err != nil { if err != nil {
return channels return channels
} }
if withCategories { if withCategories {
categories := make(map[string][]*discordgo.Channel) categories := make(map[string][]discord.Channel)
for _, channel := range guild.Channels { for _, channel := range guildChannels {
if channel.Type != discordgo.ChannelTypeGuildText && channel.Type != discordgo.ChannelTypeGuildNews { if channel.Type != discord.GuildText && channel.Type != discord.GuildAnnouncement {
continue continue
} }
perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID) perms := discord.CalcOverwrites(*guild, channel, *selfMember)
if err != nil {
continue
}
private := perms & discordgo.PermissionViewChannel == 0 private := !perms.Has(discord.PermissionViewChannel)
if private && !withPrivate { if private && !withPrivate {
continue continue
} }
categoryID := "0" categoryID := "0"
if channel.ParentID != "" { if channel.ParentID.IsValid() {
categoryID = channel.ParentID categoryID = channel.ParentID.String()
} }
_, has := categories[categoryID] _, has := categories[categoryID]
if !has { if !has {
categories[categoryID] = make([]*discordgo.Channel, 0) categories[categoryID] = make([]discord.Channel, 0)
} }
categories[categoryID] = append(categories[categoryID], channel) categories[categoryID] = append(categories[categoryID], channel)
} }
for id, channels := range categories { for id, category := range categories {
sort.Slice(channels, func(i, j int) bool { // sort channels by position
return channels[i].Position < channels[j].Position sort.Slice(category, func(i, j int) bool {
return category[i].Position < category[j].Position
}) })
categoryChannels := make([]*discordgo.Channel, 0) categoryChannels := make([]discord.Channel, 0)
// append category channel to top
if id != "0" { if id != "0" {
for _, channel := range guild.Channels { parsedCategoryId, err := discord.ParseSnowflake(id)
if channel.ID == id { if err != nil {
continue
}
for _, channel := range guildChannels {
if channel.ID == discord.ChannelID(parsedCategoryId) {
categoryChannels = append(categoryChannels, channel) categoryChannels = append(categoryChannels, channel)
break break
} }
} }
} }
for _, channel := range channels {
// append channels
for _, channel := range category {
categoryChannels = append(categoryChannels, channel) categoryChannels = append(categoryChannels, channel)
} }
categories[id] = categoryChannels categories[id] = categoryChannels
} }
keys := make([]string, 0, len(categories) - 1) keys := make([]string, 0)
for id := range categories { for id := range categories {
if id == "0" { if id == "0" {
continue continue
@ -125,12 +163,15 @@ func GetSortedChannels(session *discordgo.Session, guildId string, withCategorie
keys = append(keys, id) keys = append(keys, id)
} }
sort.Slice(keys, func(i, j int) bool { sort.Slice(keys, func(i, j int) bool {
ca, _ := session.State.Channel(keys[i]) pa, _ := discord.ParseSnowflake(keys[i])
cb, _ := session.State.Channel(keys[j]) pb, _ := discord.ParseSnowflake(keys[i])
ca, _ := client.ChannelStore.Channel(discord.ChannelID(pa))
cb, _ := client.ChannelStore.Channel(discord.ChannelID(pb))
return ca.Position < cb.Position return ca.Position < cb.Position
}) })
sortedCategories := make(map[string][]*discordgo.Channel) sortedCategories := make(map[string][]discord.Channel)
sortedCategories["0"] = categories["0"] sortedCategories["0"] = categories["0"]
for _, id := range keys { for _, id := range keys {
@ -143,17 +184,14 @@ func GetSortedChannels(session *discordgo.Session, guildId string, withCategorie
} }
} }
} else { } else {
for _, channel := range guild.Channels { for _, channel := range guildChannels {
if channel.Type != discordgo.ChannelTypeGuildText && channel.Type != discordgo.ChannelTypeGuildNews { if channel.Type != discord.GuildText && channel.Type != discord.GuildAnnouncement {
continue continue
} }
perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID) perms := discord.CalcOverwrites(*guild, channel, *selfMember)
if err != nil {
continue
}
private := perms & discordgo.PermissionViewChannel == 0 private := !perms.Has(discord.PermissionViewChannel)
if private && !withPrivate { if private && !withPrivate {
continue continue
} }
@ -169,24 +207,47 @@ func GetSortedChannels(session *discordgo.Session, guildId string, withCategorie
return channels return channels
} }
func ListChannelsCommand(session *discordgo.Session) { func ListChannelsCommand() {
client := state.GetClient()
self, err := client.MeStore.Me()
if err != nil {
fmt.Print("<failed to get self: ", err.Error(), ">\n\r")
return
}
currentGuild := state.GetCurrentGuild() currentGuild := state.GetCurrentGuild()
if currentGuild == "" { if currentGuild == "" {
fmt.Print("<not in a guild>\n\r") fmt.Print("<not in a guild>\n\r")
return return
} }
longest := 0 guildSnowflake, err := discord.ParseSnowflake(currentGuild)
channels := GetSortedChannels(session, currentGuild, true, false)
for _, channel := range channels {
perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID)
if err != nil { if err != nil {
continue fmt.Print("<failed to parse current guild id: ", err.Error(), ">\n\r")
return
} }
private := perms & discordgo.PermissionViewChannel == 0 parsedGuildId := discord.GuildID(guildSnowflake)
category := channel.Type == discordgo.ChannelTypeGuildCategory guild, err := client.GuildStore.Guild(parsedGuildId)
if err != nil {
fmt.Print("<failed to get current guild: ", err.Error(), ">\n\r")
return
}
selfMember, err := client.MemberStore.Member(parsedGuildId, self.ID)
if err != nil {
fmt.Print("<failed to get self member: ", err.Error(), ">\n\r")
return
}
longest := 0
channels := GetSortedChannels(currentGuild, true, false)
for _, channel := range channels {
perms := discord.CalcOverwrites(*guild, channel, *selfMember)
private := !perms.Has(discord.PermissionViewChannel)
category := channel.Type == discord.GuildCategory
catLen := 0 catLen := 0
if category { if category {
@ -208,13 +269,10 @@ func ListChannelsCommand(session *discordgo.Session) {
fmt.Printf(" %*s created topic\n\r", longest, "channel-name") fmt.Printf(" %*s created topic\n\r", longest, "channel-name")
fmt.Print(strings.Repeat("-", 80) + "\n\r") fmt.Print(strings.Repeat("-", 80) + "\n\r")
for _, channel := range channels { for _, channel := range channels {
perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID) perms := discord.CalcOverwrites(*guild, channel, *selfMember)
if err != nil {
continue
}
private := perms & discordgo.PermissionViewChannel == 0 private := !perms.Has(discord.PermissionViewChannel)
category := channel.Type == discordgo.ChannelTypeGuildCategory category := channel.Type == discord.GuildCategory
topic := REGEX_EMOTE.ReplaceAllString(channel.Topic, ":$1:") topic := REGEX_EMOTE.ReplaceAllString(channel.Topic, ":$1:")
topic = strings.ReplaceAll(topic, "\n", " ") topic = strings.ReplaceAll(topic, "\n", " ")
name := channel.Name name := channel.Name
@ -237,10 +295,8 @@ func ListChannelsCommand(session *discordgo.Session) {
} }
created := "??-???-??" created := "??-???-??"
timestamp, err := discordgo.SnowflakeTimestamp(channel.ID) timestamp := channel.CreatedAt()
if err == nil { created = timestamp.UTC().Format("02-Jan-06")
created = timestamp.Format("02-Jan-06")
}
fmt.Printf(" %*s %s %s\n\r", longest, name, created, topic) fmt.Printf(" %*s %s %s\n\r", longest, name, created, topic)
} }
@ -251,11 +307,13 @@ func ListChannelsCommand(session *discordgo.Session) {
type ListedMember struct { type ListedMember struct {
Name string Name string
Bot bool Bot bool
Status discordgo.Status Status discord.Status
Position int Position int
} }
func ListUsersCommand(session *discordgo.Session) { func ListUsersCommand() {
client := state.GetClient()
currentGuild := state.GetCurrentGuild() currentGuild := state.GetCurrentGuild()
currentChannel := state.GetCurrentChannel() currentChannel := state.GetCurrentChannel()
@ -268,37 +326,49 @@ func ListUsersCommand(session *discordgo.Session) {
return return
} }
guild, err := session.State.Guild(state.GetCurrentGuild()) parsedGuildId, err := discord.ParseSnowflake(currentGuild)
if err != nil { if err != nil {
fmt.Print("<failed to parse guild id: ", err.Error(), ">\n\r")
return return
} }
channel, err := session.State.Channel(currentChannel) parsedChannelId, err := discord.ParseSnowflake(currentChannel)
if err != nil { if err != nil {
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
return return
} }
fmt.Print("\n\r") guild, err := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
fmt.Printf("[you are in '%s' in '#%s' among %d]\n\r", guild.Name, channel.Name, guild.MemberCount) if err != nil {
fmt.Print("\n\r") fmt.Print("<failed to get guild: ", err.Error(), ">\n\r")
return
}
channel, err := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
if err != nil {
fmt.Print("<failed to get channel: ", err.Error(), ">\n\r")
return
}
longest := 0 longest := 0
sortedMembers := make([]ListedMember, 0) sortedMembers := make([]ListedMember, 0)
for _, presence := range guild.Presences { presences, err := client.Presences(guild.ID)
if presence.Status == discordgo.StatusOffline {
continue
}
perms, err := session.State.UserChannelPermissions(presence.User.ID, currentChannel)
if err != nil { if err != nil {
fmt.Print("<failed to get presences: ", err.Error(), ">\n\r")
return
}
for _, presence := range presences {
if presence.Status == discord.OfflineStatus {
continue continue
} }
if perms & discordgo.PermissionViewChannel == 0 { member, err := client.MemberStore.Member(guild.ID, presence.User.ID)
if err != nil {
continue continue
} }
member, err := session.State.Member(currentGuild, presence.User.ID) perms := discord.CalcOverwrites(*guild, *channel, *member)
if err != nil { if !perms.Has(discord.PermissionViewChannel) {
continue continue
} }
@ -308,8 +378,8 @@ func ListUsersCommand(session *discordgo.Session) {
} }
position := 0 position := 0
for _, id := range member.Roles { for _, id := range member.RoleIDs {
role, err := session.State.Role(currentGuild, id) role, err := client.RoleStore.Role(guild.ID, id)
if err != nil { if err != nil {
continue continue
} }
@ -327,6 +397,10 @@ func ListUsersCommand(session *discordgo.Session) {
}) })
} }
fmt.Print("\n\r")
fmt.Printf("[you are in '%s' in '#%s' among %d]\n\r", guild.Name, channel.Name, len(sortedMembers))
fmt.Print("\n\r")
membersByPosition := make(map[int][]ListedMember) membersByPosition := make(map[int][]ListedMember)
for _, member := range sortedMembers { for _, member := range sortedMembers {
_, has := membersByPosition[member.Position] _, has := membersByPosition[member.Position]
@ -359,14 +433,17 @@ func ListUsersCommand(session *discordgo.Session) {
index := 0 index := 0
for _, position := range positions { for _, position := range positions {
members := membersByPosition[position] members := membersByPosition[position]
if len(members) > 150 {
continue
}
for _, member := range members { for _, member := range members {
statusColor := "reset" statusColor := "reset"
if member.Status == discordgo.StatusOnline { if member.Status == discord.OnlineStatus {
statusColor = "green+b" statusColor = "green+b"
} else if member.Status == discordgo.StatusIdle { } else if member.Status == discord.IdleStatus {
statusColor = "yellow+b" statusColor = "yellow+b"
} else if member.Status == discordgo.StatusDoNotDisturb { } else if member.Status == discord.DoNotDisturbStatus {
statusColor = "red+b" statusColor = "red+b"
} }
@ -410,16 +487,24 @@ func ListUsersCommand(session *discordgo.Session) {
} }
} }
func SwitchGuild(session *discordgo.Session, input string) { func SwitchGuild(input string) {
client := state.GetClient()
if input == "" { if input == "" {
ListChannelsCommand(session) ListChannelsCommand()
ListUsersCommand(session) ListUsersCommand()
} else { } else {
target := "" target := ""
for _, guild := range session.State.Guilds { guilds, err := client.GuildStore.Guilds()
if err != nil {
fmt.Print("<failed to get guilds: ", err.Error(), ">\n\r")
return
}
for _, guild := range guilds {
if strings.Index(strings.ToLower(guild.Name), strings.ToLower(input)) > -1 { if strings.Index(strings.ToLower(guild.Name), strings.ToLower(input)) > -1 {
target = guild.ID target = guild.ID.String()
break; break;
} }
} }
@ -430,31 +515,33 @@ func SwitchGuild(session *discordgo.Session, input string) {
state.SetCurrentGuild(target) state.SetCurrentGuild(target)
last := state.GetLastChannel(target) last := state.GetLastChannel(target)
if last == "" { if last == "" {
channels := GetSortedChannels(session, target, false, false) channels := GetSortedChannels(target, false, false)
if len(channels) > 0 {
topChannel := channels[0] topChannel := channels[0]
state.SetCurrentChannel(topChannel.ID) state.SetCurrentChannel(topChannel.ID.String())
state.SetLastChannel(target, topChannel.ID) state.SetLastChannel(target, topChannel.ID.String())
}
} else { } else {
state.SetCurrentChannel(last) state.SetCurrentChannel(last)
} }
ListChannelsCommand(session) ListChannelsCommand()
ListUsersCommand(session) ListUsersCommand()
lib.UpdatePresence(session) lib.UpdatePresence()
} }
} }
} }
func SwitchGuildsCommand(session *discordgo.Session) { func SwitchGuildsCommand() {
lib.MakePrompt(session, ":guild> ", false, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(":guild> ", false, func(input string, interrupt bool) {
fmt.Print("\r") fmt.Print("\r")
SwitchGuild(session, input) SwitchGuild(input)
}) })
} }
func SwitchChannelsCommand(session *discordgo.Session) { func SwitchChannelsCommand() {
currentGuild := state.GetCurrentGuild() currentGuild := state.GetCurrentGuild()
if currentGuild == "" { if currentGuild == "" {
@ -462,18 +549,18 @@ func SwitchChannelsCommand(session *discordgo.Session) {
return return
} }
lib.MakePrompt(session, ":channel> ", false, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(":channel> ", false, func(input string, interrupt bool) {
fmt.Print("\r") fmt.Print("\r")
if input == "" { if input == "" {
ListUsersCommand(session) ListUsersCommand()
} else { } else {
target := "" target := ""
channels := GetSortedChannels(session, currentGuild, false, false) channels := GetSortedChannels(currentGuild, false, false)
for _, channel := range channels { for _, channel := range channels {
if strings.Index(strings.ToLower(channel.Name), strings.ToLower(input)) > -1 { if strings.Index(strings.ToLower(channel.Name), strings.ToLower(input)) > -1 {
target = channel.ID target = channel.ID.String()
break break
} }
} }
@ -484,9 +571,9 @@ func SwitchChannelsCommand(session *discordgo.Session) {
state.SetCurrentChannel(target) state.SetCurrentChannel(target)
state.SetLastChannel(currentGuild, target) state.SetLastChannel(currentGuild, target)
ListUsersCommand(session) ListUsersCommand()
lib.UpdatePresence(session) lib.UpdatePresence()
} }
} }
}) })

View file

@ -8,7 +8,6 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo"
"github.com/mgutz/ansi" "github.com/mgutz/ansi"
) )
@ -43,7 +42,7 @@ func lessLower(sa, sb string) bool {
} }
} }
func HelpCommand(session *discordgo.Session) { func HelpCommand() {
noColor := state.HasNoColor() noColor := state.HasNoColor()
fmt.Println("\r\nCOMcord (c)left 2023\n\r") fmt.Println("\r\nCOMcord (c)left 2023\n\r")

View file

@ -8,11 +8,19 @@ import (
"github.com/Cynosphere/comcord/lib" "github.com/Cynosphere/comcord/lib"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
) )
func GetHistory(session *discordgo.Session, limit int, channel string) { func GetHistory(limit int, channel string) {
messages, err := session.ChannelMessages(channel, 100, "", "", "") client := state.GetClient()
parsedChannelId, err := discord.ParseSnowflake(channel)
if err != nil {
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
return
}
messages, err := client.Messages(discord.ChannelID(parsedChannelId), 50)
if err != nil { if err != nil {
fmt.Print("<failed to get messages: ", err.Error(), ">\n\r") fmt.Print("<failed to get messages: ", err.Error(), ">\n\r")
return return
@ -26,7 +34,7 @@ func GetHistory(session *discordgo.Session, limit int, channel string) {
lines := make([]string, 0) lines := make([]string, 0)
for _, msg := range messages { for _, msg := range messages {
msgLines := lib.ProcessMessage(session, msg, lib.MessageOptions{NoColor: true, InHistory: true}) msgLines := lib.ProcessMessage(msg, lib.MessageOptions{NoColor: true, InHistory: true})
for _, line := range msgLines { for _, line := range msgLines {
lines = append(lines, line) lines = append(lines, line)
} }
@ -41,43 +49,43 @@ func GetHistory(session *discordgo.Session, limit int, channel string) {
fmt.Print("--Review-Complete", strings.Repeat("-", 63), "\n\r") fmt.Print("--Review-Complete", strings.Repeat("-", 63), "\n\r")
} }
func HistoryCommand(session *discordgo.Session) { func HistoryCommand() {
currentChannel := state.GetCurrentChannel() currentChannel := state.GetCurrentChannel()
if currentChannel == "" { if currentChannel == "" {
fmt.Print("<not in a channel>\n\r") fmt.Print("<not in a channel>\n\r")
return return
} }
GetHistory(session, 20, currentChannel) GetHistory(20, currentChannel)
} }
func ExtendedHistoryCommand(session *discordgo.Session) { func ExtendedHistoryCommand() {
currentChannel := state.GetCurrentChannel() currentChannel := state.GetCurrentChannel()
if currentChannel == "" { if currentChannel == "" {
fmt.Print("<not in a channel>\n\r") fmt.Print("<not in a channel>\n\r")
return return
} }
lib.MakePrompt(session, ":lines> ", false, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(":lines> ", false, func(input string, interrupt bool) {
fmt.Print("\r") fmt.Print("\r")
limit, err := strconv.Atoi(input) limit, err := strconv.Atoi(input)
if err != nil { if err != nil {
fmt.Print("<not a number>\n\r") fmt.Print("<not a number>\n\r")
} else { } else {
GetHistory(session, limit, currentChannel) GetHistory(limit, currentChannel)
} }
}) })
} }
func PeekHistory(session *discordgo.Session, guild, channel string) { func PeekHistory(guild, channel string) {
target := "" target := ""
channels := GetSortedChannels(session, guild, false, false) channels := GetSortedChannels(guild, false, false)
for _, c := range channels { for _, c := range channels {
if strings.Index(strings.ToLower(c.Name), strings.ToLower(channel)) > -1 { if strings.Index(strings.ToLower(c.Name), strings.ToLower(channel)) > -1 {
target = c.ID target = c.ID.String()
break break
} }
} }
@ -85,36 +93,44 @@ func PeekHistory(session *discordgo.Session, guild, channel string) {
if target == "" { if target == "" {
fmt.Print("<channel not found>\n\r") fmt.Print("<channel not found>\n\r")
} else { } else {
GetHistory(session, 20, target) GetHistory(20, target)
} }
} }
func PeekCommand(session *discordgo.Session) { func PeekCommand() {
currentGuild := state.GetCurrentGuild() currentGuild := state.GetCurrentGuild()
if currentGuild == "" { if currentGuild == "" {
fmt.Print("<not in a guild>\n\r") fmt.Print("<not in a guild>\n\r")
return return
} }
lib.MakePrompt(session, ":peek> ", false, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(":peek> ", false, func(input string, interrupt bool) {
fmt.Print("\r") fmt.Print("\r")
if input != "" { if input != "" {
PeekHistory(session, currentGuild, input) PeekHistory(currentGuild, input)
} }
}) })
} }
func CrossPeekCommand(session *discordgo.Session) { func CrossPeekCommand() {
lib.MakePrompt(session, ":guild> ", false, func(session *discordgo.Session, input string, interrupt bool) { client := state.GetClient()
lib.MakePrompt(":guild> ", false, func(input string, interrupt bool) {
fmt.Print("\r") fmt.Print("\r")
if input != "" { if input != "" {
targetGuild := "" targetGuild := ""
for _, guild := range session.State.Guilds { guilds, err := client.GuildStore.Guilds()
if err != nil {
fmt.Print("<failed to get guilds: ", err.Error(), ">\n\r")
return
}
for _, guild := range guilds {
if strings.Index(strings.ToLower(guild.Name), strings.ToLower(input)) > -1 { if strings.Index(strings.ToLower(guild.Name), strings.ToLower(input)) > -1 {
targetGuild = guild.ID targetGuild = guild.ID.String()
break; break;
} }
} }
@ -122,11 +138,11 @@ func CrossPeekCommand(session *discordgo.Session) {
if targetGuild == "" { if targetGuild == "" {
fmt.Print("<guild not found>\n\r") fmt.Print("<guild not found>\n\r")
} else { } else {
lib.MakePrompt(session, ":peek> ", false, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(":peek> ", false, func(input string, interrupt bool) {
fmt.Print("\r") fmt.Print("\r")
if input != "" { if input != "" {
PeekHistory(session, targetGuild, input) PeekHistory(targetGuild, input)
} }
}) })
} }

View file

@ -1,11 +1,9 @@
package commands package commands
import "github.com/bwmarrin/discordgo"
var commandMap map[string]Command var commandMap map[string]Command
type Command struct { type Command struct {
Run func(*discordgo.Session) Run func()
Description string Description string
} }
@ -76,6 +74,11 @@ func Setup() {
Run: CrossPeekCommand, Run: CrossPeekCommand,
Description: "cross-guild peek", Description: "cross-guild peek",
} }
commandMap["+"] = Command{
Run: TimeCommand,
Description: "current time",
}
} }
func GetCommand(key string) (Command, bool) { func GetCommand(key string) (Command, bool) {

View file

@ -4,11 +4,13 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/bwmarrin/discordgo" "github.com/Cynosphere/comcord/state"
) )
func QuitCommand(session *discordgo.Session) { func QuitCommand() {
client := state.GetClient()
fmt.Print("Unlinking TTY...\n\r") fmt.Print("Unlinking TTY...\n\r")
session.Close() client.Close()
os.Exit(0) os.Exit(0)
} }

View file

@ -7,43 +7,64 @@ import (
"github.com/Cynosphere/comcord/lib" "github.com/Cynosphere/comcord/lib"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/mgutz/ansi" "github.com/mgutz/ansi"
) )
func SendMode(session *discordgo.Session) { func SendMode() {
client := state.GetClient()
channelId := state.GetCurrentChannel() channelId := state.GetCurrentChannel()
if channelId == "" { if channelId == "" {
fmt.Print("<not in a channel>\n\r") fmt.Print("<not in a channel>\n\r")
return return
} }
parsedChannelId, err := discord.ParseSnowflake(channelId)
if err != nil {
fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
return
}
channel, err := session.State.Channel(channelId) channel, err := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
if err != nil { if err != nil {
fmt.Print("<error getting channel: ", err.Error(), ">\n\r") fmt.Print("<error getting channel: ", err.Error(), ">\n\r")
return return
} }
perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID) guild, err := client.GuildStore.Guild(channel.GuildID)
if err != nil { if err != nil {
fmt.Print("<failed to check permissions: ", err.Error(), ">\n\r") fmt.Print("<failed to get current guild: ", err.Error(), ">\n\r")
return return
} }
if perms & discordgo.PermissionSendMessages == 0 { self, err := client.MeStore.Me()
if err != nil {
fmt.Print("<failed to get self: ", err.Error(), ">\n\r")
return
}
selfMember, err := client.MemberStore.Member(guild.ID, self.ID)
if err != nil {
fmt.Print("<failed to get self as member: ", err.Error(), ">\n\r")
return
}
perms := discord.CalcOverwrites(*guild, *channel, *selfMember)
if !perms.Has(discord.PermissionSendMessages) {
fmt.Print("<you do not have permission to send messages here>\n\r") fmt.Print("<you do not have permission to send messages here>\n\r")
return return
} }
length := utf8.RuneCountInString(session.State.User.Username) + 2 length := utf8.RuneCountInString(self.Username) + 2
curLength := state.GetNameLength() curLength := state.GetNameLength()
prompt := fmt.Sprintf("[%s]%s", session.State.User.Username, strings.Repeat(" ", (curLength - length) + 1)) prompt := fmt.Sprintf("[%s]%s", self.Username, strings.Repeat(" ", (curLength - length) + 1))
if !state.HasNoColor() { if !state.HasNoColor() {
prompt = ansi.Color(prompt, "cyan+b") prompt = ansi.Color(prompt, "cyan+b")
} }
lib.MakePrompt(session, prompt, true, func(session *discordgo.Session, input string, interrupt bool) { lib.MakePrompt(prompt, true, func(input string, interrupt bool) {
if input == "" { if input == "" {
if interrupt { if interrupt {
fmt.Print("^C<no message sent>\n\r") fmt.Print("^C<no message sent>\n\r")
@ -52,7 +73,7 @@ func SendMode(session *discordgo.Session) {
} }
} else { } else {
fmt.Print(prompt, input, "\n\r") fmt.Print(prompt, input, "\n\r")
_, err := session.ChannelMessageSend(channelId, input) _, err := client.SendMessage(channel.ID, input)
if err != nil { if err != nil {
fmt.Print("<failed to send message: ", err, ">\n\r") fmt.Print("<failed to send message: ", err, ">\n\r")

12
commands/time.go Normal file
View file

@ -0,0 +1,12 @@
package commands
import (
"fmt"
"time"
)
func TimeCommand() {
now := time.Now().UTC()
fmt.Printf("%s\n\r", now.Format("[Mon 02-Jan-06 15:04:05]"))
}

View file

@ -1,9 +1,11 @@
package events package events
import "github.com/bwmarrin/discordgo" import (
"github.com/diamondburned/ningen/v3"
)
func Setup(session *discordgo.Session) { func Setup(session *ningen.State) {
session.AddHandlerOnce(Ready) session.PreHandler.AddHandler(Ready)
session.AddHandler(MessageCreate) session.PreHandler.AddHandler(MessageCreate)
session.AddHandler(MessageUpdate) session.PreHandler.AddHandler(MessageUpdate)
} }

View file

@ -5,65 +5,84 @@ import (
"github.com/Cynosphere/comcord/lib" "github.com/Cynosphere/comcord/lib"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/discord"
) )
func MessageCreate(session *discordgo.Session, msg *discordgo.MessageCreate) { func MessageCreate(msg *gateway.MessageCreateEvent) {
if msg.Author.ID == session.State.User.ID { client := state.GetClient()
return self, err := client.MeStore.Me()
}
channel, err := session.State.Channel(msg.ChannelID)
if err != nil { if err != nil {
return return
} }
isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM if msg.Author.ID == self.ID {
return
}
channel, err := client.ChannelStore.Channel(msg.ChannelID)
if err != nil {
return
}
isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
if state.IsInPrompt() { if state.IsInPrompt() {
state.AddMessageToQueue(msg.Message) state.AddMessageToQueue(msg.Message)
} else { } else {
lines := lib.ProcessMessage(session, msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()}) lines := lib.ProcessMessage(msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()})
for _, line := range lines { for _, line := range lines {
fmt.Print(line) fmt.Print(line)
} }
} }
if isDM { if isDM {
state.SetLastDM(msg.ChannelID) state.SetLastDM(msg.ChannelID.String())
} }
} }
func MessageUpdate(session *discordgo.Session, msg *discordgo.MessageUpdate) { func MessageUpdate(msg *gateway.MessageUpdateEvent) {
if msg.Author == nil { client := state.GetClient()
return self, err := client.MeStore.Me()
}
if msg.Author.ID == session.State.User.ID {
return
}
if msg.BeforeUpdate != nil && msg.Content == msg.BeforeUpdate.Content {
return
}
channel, err := session.State.Channel(msg.ChannelID)
if err != nil { if err != nil {
return return
} }
isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM if msg.Author.ID == self.ID {
return
}
old, err := client.MessageStore.Message(msg.ChannelID, msg.ID)
if err != nil {
return
}
if msg.Content == old.Content {
return
}
// dont process embed updates as messages
if !msg.EditedTimestamp.IsValid() {
return
}
channel, err := client.ChannelStore.Channel(msg.ChannelID)
if err != nil {
return
}
isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
if state.IsInPrompt() { if state.IsInPrompt() {
state.AddMessageToQueue(msg.Message) state.AddMessageToQueue(msg.Message)
} else { } else {
lines := lib.ProcessMessage(session, msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()}) lines := lib.ProcessMessage(msg.Message, lib.MessageOptions{NoColor: state.HasNoColor()})
for _, line := range lines { for _, line := range lines {
fmt.Print(line) fmt.Print(line)
} }
} }
if isDM { if isDM {
state.SetLastDM(msg.ChannelID) state.SetLastDM(msg.ChannelID.String())
} }
} }

View file

@ -6,27 +6,41 @@ import (
"github.com/Cynosphere/comcord/commands" "github.com/Cynosphere/comcord/commands"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/mgutz/ansi" "github.com/mgutz/ansi"
) )
func Ready(session *discordgo.Session, event *discordgo.Ready) { func Ready(event *gateway.ReadyEvent) {
fmt.Printf("\rLogged in as: %s\n\r", ansi.Color(fmt.Sprintf("%s (%s)", session.State.User.Username, session.State.User.ID), "yellow")) client := state.GetClient()
self, err := client.Me()
if err != nil {
fmt.Print("\r% Failed to get self: ", err.Error(), "\n\r")
return
}
state.SetNameLength(utf8.RuneCountInString(session.State.User.Username) + 2) fmt.Printf("\rLogged in as: %s\n\r", ansi.Color(fmt.Sprintf("%s (%s)", self.Username, self.ID), "yellow"))
commands.ListGuildsCommand(session) state.SetNameLength(utf8.RuneCountInString(self.Username) + 2)
commands.ListGuildsCommand()
defaultGuild := state.GetConfigValue("defaultGuild") defaultGuild := state.GetConfigValue("defaultGuild")
defaultChannel := state.GetConfigValue("defaultChannel") defaultChannel := state.GetConfigValue("defaultChannel")
if defaultGuild != "" { if defaultGuild != "" {
guild, err := session.State.Guild(defaultGuild) parsedGuildId, err := discord.ParseSnowflake(defaultGuild)
if err != nil {
fmt.Print("\r% Failed to parse guild ID: ", err.Error(), "\n\r")
return
}
guild, err := client.Guild(discord.GuildID(parsedGuildId))
if err == nil { if err == nil {
if defaultChannel != "" { if defaultChannel != "" {
state.SetCurrentChannel(defaultChannel) state.SetCurrentChannel(defaultChannel)
state.SetLastChannel(defaultGuild, defaultChannel) state.SetLastChannel(defaultGuild, defaultChannel)
} }
commands.SwitchGuild(session, guild.Name) commands.SwitchGuild(guild.Name)
} else { } else {
fmt.Println("\r% This account is not in the defined default guild.") fmt.Println("\r% This account is not in the defined default guild.")
} }

10
go.mod
View file

@ -4,18 +4,24 @@ go 1.20
require ( require (
atomicgo.dev/keyboard v0.2.9 // indirect atomicgo.dev/keyboard v0.2.9 // indirect
github.com/bwmarrin/discordgo v0.27.1 // indirect
github.com/containerd/console v1.0.3 // indirect github.com/containerd/console v1.0.3 // indirect
github.com/diamondburned/arikawa v1.3.2 // indirect
github.com/diamondburned/arikawa/v3 v3.3.1 // indirect
github.com/diamondburned/ningen/v3 v3.0.0 // indirect
github.com/ergochat/readline v0.0.5 // indirect github.com/ergochat/readline v0.0.5 // indirect
github.com/gorilla/schema v1.2.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect
github.com/kopoli/go-terminal-size v0.0.0-20170219200355-5c97524c8b54 // indirect github.com/kopoli/go-terminal-size v0.0.0-20170219200355-5c97524c8b54 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect github.com/twmb/murmur3 v1.1.3 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/sys v0.10.0 // indirect golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
) )

246
go.sum
View file

@ -1,5 +1,24 @@
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8= atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ= atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
@ -10,22 +29,74 @@ github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYew
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk= github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/diamondburned/arikawa v1.3.2 h1:ftWgP95IJGXNvCvtO5x0QBYsnFSnIBY0SvDdGoC3ILA=
github.com/diamondburned/arikawa v1.3.2/go.mod h1:nIhVIatzTQhPUa7NB8w4koG1RF9gYbpAr8Fj8sKq660=
github.com/diamondburned/arikawa/v3 v3.1.1-0.20221103093025-87c479a2dcd4/go.mod h1:5jBSNnp82Z/EhsKa6Wk9FsOqSxfVkNZDTDBPOj47LpY=
github.com/diamondburned/arikawa/v3 v3.3.1 h1:puqs7mog383RJnUfRSiug5K+z/pC0Cbuq4yGJMQrQWg=
github.com/diamondburned/arikawa/v3 v3.3.1/go.mod h1:+ifmDonP/JdBiUOzZmVReEjPTHDUSkyqqRRmjSf9NE8=
github.com/diamondburned/ningen v1.0.0 h1:fr+7oDWA0Db73CuVeLY8SScWdW6ft/aWwkULXD0flKw=
github.com/diamondburned/ningen v1.0.0/go.mod h1:TcvJV0bK4bp7t+7m29/Tz9dCqgA0sJBKM/Igt0WkvT4=
github.com/diamondburned/ningen/v3 v3.0.0 h1:S7DF+AwOt/zuFsBMAu00mtE8MfuYqaTtDii6iJPX758=
github.com/diamondburned/ningen/v3 v3.0.0/go.mod h1:wMe9WZQiFgkH5Slr5xK8XBBqMJxWTfRDZU82wPney4Y=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ergochat/readline v0.0.5 h1:PlmCLW9HUTnVfhFburg65pQUDKb0LB43G8hS+ygEkp8= github.com/ergochat/readline v0.0.5 h1:PlmCLW9HUTnVfhFburg65pQUDKb0LB43G8hS+ygEkp8=
github.com/ergochat/readline v0.0.5/go.mod h1:8RNv74chpO0eTm6rdD1H6WZGihL5rJ+RfSlhv4fIfjg= github.com/ergochat/readline v0.0.5/go.mod h1:8RNv74chpO0eTm6rdD1H6WZGihL5rJ+RfSlhv4fIfjg=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 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.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
@ -46,7 +117,10 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI= github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg= github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
@ -56,28 +130,119 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/twmb/murmur3 v1.1.3 h1:D83U0XYKcHRYwYIpBKf3Pks91Z0Byda/9SJ8B6EMRcA=
github.com/twmb/murmur3 v1.1.3/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448 h1:AzpBtmgdXa3uznrb3esNeEoaLqtNEwckRmaUH0qWD6w= github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448 h1:AzpBtmgdXa3uznrb3esNeEoaLqtNEwckRmaUH0qWD6w=
github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM= github.com/wader/readline v0.0.0-20230307172220-bcb7158e7448/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.2/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= 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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
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=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211001092434-39dca1131b70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
@ -86,17 +251,98 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View file

@ -9,7 +9,7 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/mgutz/ansi" "github.com/mgutz/ansi"
) )
@ -19,12 +19,12 @@ var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
type MessageOptions struct { type MessageOptions struct {
Content string Content string
Name string Name string
Channel string Channel discord.ChannelID
Bot bool Bot bool
Webhook bool Webhook bool
Attachments []*discordgo.MessageAttachment Attachments []discord.Attachment
Stickers []*discordgo.Sticker Stickers []discord.StickerItem
Reply *discordgo.Message Reply *discord.Message
Timestamp time.Time Timestamp time.Time
IsMention bool IsMention bool
IsDM bool IsDM bool
@ -35,10 +35,12 @@ type MessageOptions struct {
InHistory bool InHistory bool
} }
func FormatMessage(session *discordgo.Session, options MessageOptions) []string { func FormatMessage(options MessageOptions) []string {
client := state.GetClient()
lines := make([]string, 0) lines := make([]string, 0)
timestamp := options.Timestamp.Format("[15:04:05]") timestamp := options.Timestamp.UTC().Format("[15:04:05]")
nameLength := utf8.RuneCountInString(options.Name) + 2 nameLength := utf8.RuneCountInString(options.Name) + 2
stateNameLength := state.GetNameLength() stateNameLength := state.GetNameLength()
@ -55,7 +57,7 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
headerLength := 6 + utf8.RuneCountInString(options.Reply.Author.Username) headerLength := 6 + utf8.RuneCountInString(options.Reply.Author.Username)
content, _ := options.Reply.ContentWithMoreMentionsReplaced(session) content := options.Reply.Content
replyContent := strings.ReplaceAll(content, "\n", " ") replyContent := strings.ReplaceAll(content, "\n", " ")
// TODO: markdown // TODO: markdown
@ -71,7 +73,7 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
replyContent = strings.TrimSpace(replyContent + fmt.Sprintf(" <%d attachment%s>", attachmentCount, attachmentPlural)) replyContent = strings.TrimSpace(replyContent + fmt.Sprintf(" <%d attachment%s>", attachmentCount, attachmentPlural))
} }
stickerCount := len(options.Reply.StickerItems) stickerCount := len(options.Reply.Stickers)
if stickerCount > 0 { if stickerCount > 0 {
stickerPlural := "" stickerPlural := ""
if stickerCount > 0 { if stickerCount > 0 {
@ -161,11 +163,11 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
lines = append(lines, str + "\n\r") lines = append(lines, str + "\n\r")
} else if options.IsJoin { } else if options.IsJoin {
channel, err := session.State.Channel(options.Channel) channel, err := client.ChannelStore.Channel(options.Channel)
if err != nil { if err != nil {
return lines return lines
} }
guild, err := session.State.Guild(channel.GuildID) guild, err := client.GuildStore.Guild(channel.GuildID)
if err != nil { if err != nil {
return lines return lines
} }
@ -237,27 +239,33 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
return lines return lines
} }
func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options MessageOptions) []string { func ProcessMessage(msg discord.Message, options MessageOptions) []string {
client := state.GetClient()
lines := make([]string, 0) lines := make([]string, 0)
channel, err := session.State.Channel(msg.ChannelID) channel, err := client.ChannelStore.Channel(msg.ChannelID)
if err != nil { if err != nil {
return lines return lines
} }
guild, err := session.State.Guild(channel.GuildID) guild, err := client.GuildStore.Guild(channel.GuildID)
if err != nil { if err != nil {
return lines return lines
} }
selfMember, err := session.State.Member(guild.ID, session.State.User.ID) self, err := client.MeStore.Me()
if err != nil {
return lines
}
selfMember, err := client.MemberStore.Member(guild.ID, self.ID)
if err != nil { if err != nil {
return lines return lines
} }
hasMentionedRole := false hasMentionedRole := false
for _, role := range msg.MentionRoles { for _, role := range msg.MentionRoleIDs {
for _, selfRole := range selfMember.Roles { for _, selfRole := range selfMember.RoleIDs {
if role == selfRole { if role == selfRole {
hasMentionedRole = true hasMentionedRole = true
break; break;
@ -267,18 +275,18 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
isDirectlyMentioned := false isDirectlyMentioned := false
for _, user := range msg.Mentions { for _, user := range msg.Mentions {
if user.ID == session.State.User.ID { if user.ID == self.ID {
isDirectlyMentioned = true isDirectlyMentioned = true
break; break;
} }
} }
isPing := msg.MentionEveryone || hasMentionedRole || isDirectlyMentioned isPing := msg.MentionEveryone || hasMentionedRole || isDirectlyMentioned
isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
isEdit := msg.EditedTimestamp != nil isEdit := msg.EditedTimestamp.IsValid()
currentChannel := state.GetCurrentChannel() currentChannel := state.GetCurrentChannel()
isCurrentChannel := currentChannel == msg.ChannelID isCurrentChannel := currentChannel == msg.ChannelID.String()
if !isCurrentChannel && !isDM && !isPing && !options.InHistory { if !isCurrentChannel && !isDM && !isPing && !options.InHistory {
return lines return lines
@ -292,7 +300,7 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
str = str + "\x07\n\r" str = str + "\x07\n\r"
lines = append(lines, str) lines = append(lines, str)
} else { } else {
content, _ := msg.ContentWithMoreMentionsReplaced(session) content := msg.Content
if isEdit { if isEdit {
content = content + " (edited)" content = content + " (edited)"
} }
@ -305,19 +313,19 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
options.Name = msg.Author.Username options.Name = msg.Author.Username
options.Channel = msg.ChannelID options.Channel = msg.ChannelID
options.Bot = msg.Author.Bot options.Bot = msg.Author.Bot
options.Webhook = msg.WebhookID != "" options.Webhook = msg.WebhookID.IsValid()
options.Attachments = msg.Attachments options.Attachments = msg.Attachments
options.Stickers = msg.StickerItems options.Stickers = msg.Stickers
if i == 0 { if i == 0 {
options.Reply = msg.ReferencedMessage options.Reply = msg.ReferencedMessage
} }
options.IsMention = isPing options.IsMention = isPing
options.IsDM = isDM options.IsDM = isDM
options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin options.IsJoin = msg.Type == discord.GuildMemberJoinMessage
options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage options.IsPin = msg.Type == discord.ChannelPinnedMessage
options.IsDump = false options.IsDump = false
msgLines := FormatMessage(session, options) msgLines := FormatMessage(options)
for _, line := range msgLines { for _, line := range msgLines {
lines = append(lines, line) lines = append(lines, line)
} }
@ -327,28 +335,28 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
options.Name = msg.Author.Username options.Name = msg.Author.Username
options.Channel = msg.ChannelID options.Channel = msg.ChannelID
options.Bot = msg.Author.Bot options.Bot = msg.Author.Bot
options.Webhook = msg.WebhookID != "" options.Webhook = msg.WebhookID.IsValid()
options.Attachments = msg.Attachments options.Attachments = msg.Attachments
options.Stickers = msg.StickerItems options.Stickers = msg.Stickers
options.Reply = msg.ReferencedMessage options.Reply = msg.ReferencedMessage
options.IsMention = isPing options.IsMention = isPing
options.IsDM = isDM options.IsDM = isDM
options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin options.IsJoin = msg.Type == discord.GuildMemberJoinMessage
options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage options.IsPin = msg.Type == discord.ChannelPinnedMessage
options.IsDump = isDump options.IsDump = isDump
lines = FormatMessage(session, options) lines = FormatMessage(options)
} }
} }
return lines return lines
} }
func ProcessQueue(session *discordgo.Session) { func ProcessQueue() {
queue := state.GetMessageQueue() queue := state.GetMessageQueue()
for _, msg := range queue { for _, msg := range queue {
lines := ProcessMessage(session, msg, MessageOptions{NoColor: state.HasNoColor()}) lines := ProcessMessage(msg, MessageOptions{NoColor: state.HasNoColor()})
for _, line := range lines { for _, line := range lines {
fmt.Print(line) fmt.Print(line)
} }

View file

@ -1,98 +1,57 @@
package lib package lib
import ( import (
"context"
"fmt" "fmt"
"reflect"
"sync"
"time"
"unsafe"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/gorilla/websocket" "github.com/diamondburned/arikawa/v3/gateway"
) )
type ActivityMetadata struct { func UpdatePresence() {
ButtonURLs []string `json:"button_urls,omitempty"` client := state.GetClient()
}
type Activity struct { self, err := client.MeStore.Me()
Name string `json:"name"` if err != nil {
Type discordgo.ActivityType `json:"type"` return
//URL string `json:"url,omitempty"` }
CreatedAt time.Time `json:"created_at"`
ApplicationID string `json:"application_id,omitempty"`
State string `json:"state,omitempty"`
Details string `json:"details,omitempty"`
Timestamps discordgo.TimeStamps `json:"timestamps,omitempty"`
//Emoji discordgo.Emoji `json:"emoji,omitempty"`
//Party discordgo.Party `json:"party,omitempty"`
Assets discordgo.Assets `json:"assets,omitempty"`
//Secrets discordgo.Secrets `json:"secrets,omitempty"`
//Instance bool `json:"instance,omitempty"`
//Flags int `json:"flags,omitempty"`
Buttons []string `json:"buttons,omitempty"`
Metadata ActivityMetadata `json:"metadata,omitempty"`
}
type GatewayPresenceUpdate struct {
Since int `json:"since"`
Activities []Activity `json:"activities,omitempty"`
Status string `json:"status"`
AFK bool `json:"afk"`
Broadcast string `json:"broadcast,omitempty"`
}
type presenceOp struct {
Op int `json:"op"`
Data GatewayPresenceUpdate `json:"d"`
}
func getUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func UpdatePresence(session *discordgo.Session) {
// there is a way to send presence without reflecting to grab the websocket
// connection, but theres an issue with the serialization that because a value
// isn't being considered "null" that its trying to apply and failing because
// the default doesn't make sense in this context, even if omitempty is set
//
// this doesnt happen with bot accounts because they have certain fields
// stripped
values := reflect.ValueOf(session)
fieldWsConn := reflect.Indirect(values).FieldByName("wsConn")
fieldWsMutex := reflect.Indirect(values).FieldByName("wsMutex")
wsConn := getUnexportedField(fieldWsConn).(*websocket.Conn)
wsMutex := getUnexportedField(fieldWsMutex).(sync.Mutex)
afk := state.IsAFK() afk := state.IsAFK()
presence := GatewayPresenceUpdate{ presence := gateway.UpdatePresenceCommand{
Since: 0, Since: 0,
AFK: afk, Activities: make([]discord.Activity, 0),
Activities: make([]Activity, 0), AFK: false,
} }
currentGuild := state.GetCurrentGuild() currentGuild := state.GetCurrentGuild()
currentChannel := state.GetCurrentChannel() currentChannel := state.GetCurrentChannel()
var activity Activity parsedGuildId, err := discord.ParseSnowflake(currentGuild)
if err != nil {
return
}
parsedChannelId, err := discord.ParseSnowflake(currentChannel)
if err != nil {
return
}
var activity discord.Activity
startTime := state.GetStartTime() startTime := state.GetStartTime()
if session.State.User.Bot { if self.Bot {
activity = Activity{ activity = discord.Activity{
Type: 0, Type: discord.GameActivity,
Name: "comcord", Name: "comcord",
} }
if currentGuild != "" && currentChannel != "" { if currentGuild != "" && currentChannel != "" {
guild, guildErr := session.State.Guild(currentGuild) guild, guildErr := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
channel, channelErr := session.State.Channel(currentChannel) channel, channelErr := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
if guildErr == nil && channelErr == nil { if guildErr == nil && channelErr == nil {
activity.Type = 3 activity.Type = discord.WatchingActivity
activity.Name = fmt.Sprintf("#%s in %s | comcord", channel.Name, guild.Name) activity.Name = fmt.Sprintf("#%s in %s | comcord", channel.Name, guild.Name)
} }
} }
@ -101,33 +60,38 @@ func UpdatePresence(session *discordgo.Session) {
activity.Name = activity.Name + " [AFK]" activity.Name = activity.Name + " [AFK]"
} }
} else { } else {
activity = Activity{ parsedAppId, err := discord.ParseSnowflake("1026163285877325874")
Type: 0, if err != nil {
ApplicationID: "1026163285877325874", return
Name: "comcord",
Timestamps: discordgo.TimeStamps{
StartTimestamp: startTime.Unix(),
},
Buttons: make([]string, 0),
Metadata: ActivityMetadata{
ButtonURLs: make([]string, 0),
},
} }
activity.Buttons = append(activity.Buttons, "comcord Repo") activity = discord.Activity{
activity.Metadata.ButtonURLs = append(activity.Metadata.ButtonURLs, "https://gitdab.com/Cynosphere/comcord") Type: 0,
AppID: discord.AppID(parsedAppId),
Name: "comcord",
Timestamps: &discord.ActivityTimestamps{
Start: discord.UnixMsTimestamp(startTime.Unix()),
},
/*Buttons: make([]string, 0),
Metadata: ActivityMetadata{
ButtonURLs: make([]string, 0),
},*/
}
//activity.Buttons = append(activity.Buttons, "comcord Repo")
//activity.Metadata.ButtonURLs = append(activity.Metadata.ButtonURLs, "https://gitdab.com/Cynosphere/comcord")
if currentGuild != "" && currentChannel != "" { if currentGuild != "" && currentChannel != "" {
guild, guildErr := session.State.Guild(currentGuild) guild, guildErr := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
channel, channelErr := session.State.Channel(currentChannel) channel, channelErr := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
if guildErr == nil && channelErr == nil { if guildErr == nil && channelErr == nil {
activity.Details = fmt.Sprintf("#%s - %s", channel.Name, guild.Name) activity.Details = fmt.Sprintf("#%s - %s", channel.Name, guild.Name)
activity.Assets = discordgo.Assets{} activity.Assets = &discord.ActivityAssets{}
activity.Assets.LargeText = guild.Name activity.Assets.LargeText = guild.Name
if guild.Icon != "" { if guild.Icon != "" {
activity.Assets.LargeImageID = fmt.Sprintf("mp:icons/%s/%s.png?size=1024", guild.ID, guild.Icon) activity.Assets.LargeImage = fmt.Sprintf("mp:icons/%s/%s.png?size=1024", guild.ID, guild.Icon)
} }
} }
} }
@ -137,23 +101,20 @@ func UpdatePresence(session *discordgo.Session) {
} }
} }
activity.CreatedAt = startTime activity.CreatedAt = discord.UnixTimestamp(startTime.Unix())
presence.Activities = append(presence.Activities, activity) presence.Activities = append(presence.Activities, activity)
defaultStatus := state.GetConfigValue("defaultStatus") defaultStatus := state.GetConfigValue("defaultStatus")
if defaultStatus != "" { if defaultStatus != "" {
presence.Status = defaultStatus presence.Status = discord.Status(defaultStatus)
} else { } else {
if afk { if afk {
presence.Status = "idle" presence.Status = discord.IdleStatus
} else { } else {
presence.Status = "online" presence.Status = discord.OnlineStatus
} }
} }
op := presenceOp{3, presence} client.Gateway().Send(context.Background(), &presence)
wsMutex.Lock()
wsConn.WriteJSON(op)
wsMutex.Unlock()
} }

View file

@ -4,11 +4,10 @@ import (
"strings" "strings"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo"
"github.com/ergochat/readline" "github.com/ergochat/readline"
) )
func MakePrompt(session *discordgo.Session, prompt string, uniqueLine bool, callback func(session *discordgo.Session, input string, interrupt bool)) { func MakePrompt(prompt string, uniqueLine bool, callback func(input string, interrupt bool)) {
state.SetInPrompt(true) state.SetInPrompt(true)
state.SetPromptText(prompt) state.SetPromptText(prompt)
@ -24,10 +23,10 @@ func MakePrompt(session *discordgo.Session, prompt string, uniqueLine bool, call
interrupt := err == readline.ErrInterrupt interrupt := err == readline.ErrInterrupt
callback(session, input, interrupt) callback(input, interrupt)
state.SetInPrompt(false) state.SetInPrompt(false)
state.SetPromptText("") state.SetPromptText("")
ProcessQueue(session) ProcessQueue()
} }

79
main.go
View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
@ -12,7 +13,10 @@ import (
"github.com/Cynosphere/comcord/events" "github.com/Cynosphere/comcord/events"
"github.com/Cynosphere/comcord/rcfile" "github.com/Cynosphere/comcord/rcfile"
"github.com/Cynosphere/comcord/state" "github.com/Cynosphere/comcord/state"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/utils/handler"
"github.com/diamondburned/ningen/v3"
"golang.org/x/term" "golang.org/x/term"
) )
@ -60,7 +64,6 @@ func main() {
fmt.Println("\rType 'h' for Commands") fmt.Println("\rType 'h' for Commands")
fmt.Print("\r") fmt.Print("\r")
state.Setup(config)
commands.Setup() commands.Setup()
allowUserAccounts := config["allowUserAccounts"] == "true" allowUserAccounts := config["allowUserAccounts"] == "true"
@ -71,30 +74,26 @@ func main() {
fullToken := tokenPrefix + token fullToken := tokenPrefix + token
client, err := discordgo.New(fullToken) props := gateway.IdentifyProperties{
if err != nil {
fmt.Println("% Failed to create client:", err)
fmt.Print("\r")
os.Exit(1)
return
}
//client.LogLevel = discordgo.LogDebug
client.Identify.Intents = discordgo.IntentsAll
client.Identify.Properties = discordgo.IdentifyProperties{
OS: runtime.GOOS, OS: runtime.GOOS,
} }
statusType := config["statusType"] statusType := config["statusType"]
if statusType == "mobile" { if statusType == "mobile" {
client.Identify.Properties.Browser = "Discord Android" props.Browser = "Discord Android"
} else if statusType == "embedded" { } else if statusType == "embedded" {
client.Identify.Properties.Browser = "Discord Embedded" props.Browser = "Discord Embedded"
} else if statusType == "desktop" { } else if statusType == "desktop" {
client.Identify.Properties.Browser = "Discord Client" props.Browser = "Discord Client"
} else { } else {
client.Identify.Properties.Browser = "comcord" props.Browser = "comcord"
}
ident := gateway.IdentifyCommand{
Token: fullToken,
Properties: props,
Compress: true,
LargeThreshold: 50,
} }
status := "online" status := "online"
@ -104,39 +103,57 @@ func main() {
} }
startTime := state.GetStartTime() startTime := state.GetStartTime()
client.Identify.Presence = discordgo.GatewayStatusUpdate{ activity := discord.Activity{
Since: 0,
Status: status,
AFK: false,
Game: discordgo.Activity{
Type: 0,
Name: "comcord", Name: "comcord",
ApplicationID: "1026163285877325874", Type: discord.GameActivity,
CreatedAt: startTime, CreatedAt: discord.UnixTimestamp(startTime.Unix()),
Timestamps: &discord.ActivityTimestamps{
Start: discord.UnixMsTimestamp(startTime.Unix()),
}, },
} }
presence := gateway.UpdatePresenceCommand{
Since: 0,
Activities: make([]discord.Activity, 0),
Status: discord.Status(status),
AFK: false,
}
presence.Activities = append(presence.Activities, activity)
ident.Presence = &presence
client := ningen.NewWithIdentifier(gateway.NewIdentifier(ident))
client.PreHandler = handler.New()
client.AddIntents(gateway.IntentGuilds)
client.AddIntents(gateway.IntentGuildPresences)
client.AddIntents(gateway.IntentGuildMembers)
client.AddIntents(gateway.IntentGuildMessages)
client.AddIntents(gateway.IntentDirectMessages)
client.AddIntents(gateway.IntentMessageContent)
state.Setup(config, client)
events.Setup(client) events.Setup(client)
err = client.Open() err = client.Open(context.Background())
if err != nil { if err != nil {
fmt.Println("% Failed to connect to Discord:", err) fmt.Println("% Failed to connect to Discord:", err)
fmt.Print("\r") fmt.Print("\r")
os.Exit(1) os.Exit(1)
return return
} }
defer client.Close()
keyboard.Listen(func(key keys.Key) (stop bool, err error) { keyboard.Listen(func(key keys.Key) (stop bool, err error) {
if !state.IsInPrompt() { if !state.IsInPrompt() {
if key.Code == keys.CtrlC { if key.Code == keys.CtrlC {
commands.QuitCommand(client) commands.QuitCommand()
return true, nil return true, nil
} else { } else {
command, has := commands.GetCommand(key.String()) command, has := commands.GetCommand(key.String())
if has { if has {
command.Run(client) command.Run()
} else { } else {
commands.SendMode(client) commands.SendMode()
} }
} }
} }

View file

@ -3,11 +3,14 @@ package state
import ( import (
"time" "time"
"github.com/bwmarrin/discordgo" "github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/ningen/v3"
) )
type ComcordState struct { type ComcordState struct {
Client *ningen.State
Config map[string]string Config map[string]string
Readied bool
Connected bool Connected bool
RPCConnected bool RPCConnected bool
StartTime time.Time StartTime time.Time
@ -17,7 +20,7 @@ type ComcordState struct {
InPrompt bool InPrompt bool
PromptText string PromptText string
AFK bool AFK bool
MessageQueue []*discordgo.Message MessageQueue []discord.Message
LastChannel map[string]string LastChannel map[string]string
LastDM string LastDM string
NoColor bool NoColor bool
@ -25,9 +28,11 @@ type ComcordState struct {
var state ComcordState var state ComcordState
func Setup(config map[string]string) { func Setup(config map[string]string, client *ningen.State) {
state = ComcordState{} state = ComcordState{}
state.Client = client
state.Config = config state.Config = config
state.Readied = false
state.Connected = true state.Connected = true
state.RPCConnected = false state.RPCConnected = false
state.StartTime = time.Now() state.StartTime = time.Now()
@ -37,12 +42,24 @@ func Setup(config map[string]string) {
state.InPrompt = false state.InPrompt = false
state.PromptText = "" state.PromptText = ""
state.AFK = false state.AFK = false
state.MessageQueue = make([]*discordgo.Message, 0) state.MessageQueue = make([]discord.Message, 0)
state.LastChannel = make(map[string]string) state.LastChannel = make(map[string]string)
state.LastDM = "" state.LastDM = ""
state.NoColor = false state.NoColor = false
} }
func GetClient() *ningen.State {
return state.Client
}
func HasReadied() bool {
return state.Readied
}
func SetReadied(value bool) {
state.Readied = value
}
func IsConnected() bool { func IsConnected() bool {
return state.Connected return state.Connected
} }
@ -111,16 +128,16 @@ func SetAFK(value bool) {
state.AFK = value state.AFK = value
} }
func GetMessageQueue() []*discordgo.Message { func GetMessageQueue() []discord.Message {
return state.MessageQueue return state.MessageQueue
} }
func AddMessageToQueue(msg *discordgo.Message) { func AddMessageToQueue(msg discord.Message) {
state.MessageQueue = append(state.MessageQueue, msg) state.MessageQueue = append(state.MessageQueue, msg)
} }
func EmptyMessageQueue() { func EmptyMessageQueue() {
state.MessageQueue = make([]*discordgo.Message, 0) state.MessageQueue = make([]discord.Message, 0)
} }
func SetLastChannel(guild string, channel string) { func SetLastChannel(guild string, channel string) {