history repeats itself
discordgo -> arikawa
This commit is contained in:
		
							parent
							
								
									7af5462d54
								
							
						
					
					
						commit
						0d6a1398f0
					
				
					 19 changed files with 775 additions and 378 deletions
				
			
		| 
						 | 
				
			
			@ -2,10 +2,8 @@ package commands
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ClearCommand(session *discordgo.Session) {
 | 
			
		||||
func ClearCommand() {
 | 
			
		||||
  fmt.Print("\n\r\033[H\033[2J")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,10 +5,10 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Cynosphere/comcord/lib"
 | 
			
		||||
	"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()
 | 
			
		||||
  if channelId == "" {
 | 
			
		||||
    fmt.Print("<not in a channel>\n\r")
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ func EmoteCommand(session *discordgo.Session) {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  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 interrupt {
 | 
			
		||||
        fmt.Print("^C<no message sent>\n\r")
 | 
			
		||||
| 
						 | 
				
			
			@ -25,10 +25,18 @@ func EmoteCommand(session *discordgo.Session) {
 | 
			
		|||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      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 {
 | 
			
		||||
        fmt.Print("<failed to send message: ", err, ">\n\r")
 | 
			
		||||
        fmt.Print("<failed to send message: ", err.Error(), ">\n\r")
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // TODO: update afk state
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,9 +10,9 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Cynosphere/comcord/lib"
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/mgutz/ansi"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/discord"
 | 
			
		||||
	tsize "github.com/kopoli/go-terminal-size"
 | 
			
		||||
	"github.com/mgutz/ansi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
 | 
			
		||||
| 
						 | 
				
			
			@ -23,36 +23,39 @@ type GuildListing struct {
 | 
			
		|||
  Online int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ListGuildsCommand(session *discordgo.Session) {
 | 
			
		||||
func ListGuildsCommand() {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  longest := 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)
 | 
			
		||||
    if length > longest {
 | 
			
		||||
      longest = length
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    guildWithCounts, err := session.GuildWithCounts(guild.ID)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
    withCount, err := client.GuildWithCount(guild.ID)
 | 
			
		||||
    if err == nil {
 | 
			
		||||
      guilds = append(guilds, GuildListing{
 | 
			
		||||
        Name: guild.Name,
 | 
			
		||||
        Members: guild.MemberCount,
 | 
			
		||||
        Online: 0,
 | 
			
		||||
        Members: int(withCount.ApproximateMembers),
 | 
			
		||||
        Online: int(withCount.ApproximatePresences),
 | 
			
		||||
      })
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if guild.Name == "" && guildWithCounts.Name != "" {
 | 
			
		||||
      guild.Name = guildWithCounts.Name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
      guilds = append(guilds, GuildListing{
 | 
			
		||||
        Name: guild.Name,
 | 
			
		||||
      Members: guildWithCounts.ApproximateMemberCount,
 | 
			
		||||
      Online: guildWithCounts.ApproximatePresenceCount,
 | 
			
		||||
        Members: int(guild.ApproximateMembers),
 | 
			
		||||
        Online: int(guild.ApproximatePresences),
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fmt.Print("\n\r")
 | 
			
		||||
  fmt.Printf("  %*s  online  total\n\r", longest, "guild-name")
 | 
			
		||||
| 
						 | 
				
			
			@ -64,66 +67,89 @@ func ListGuildsCommand(session *discordgo.Session) {
 | 
			
		|||
  fmt.Print("\n\r")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetSortedChannels(session *discordgo.Session, guildId string, withCategories bool, withPrivate bool) []*discordgo.Channel {
 | 
			
		||||
  channels := make([]*discordgo.Channel, 0)
 | 
			
		||||
  guild, err := session.State.Guild(guildId)
 | 
			
		||||
func GetSortedChannels(guildId string, withCategories bool, withPrivate bool) []discord.Channel {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
  channels := make([]discord.Channel, 0)
 | 
			
		||||
 | 
			
		||||
  guildSnowflake, err := discord.ParseSnowflake(guildId)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    return channels
 | 
			
		||||
  }
 | 
			
		||||
  guildChannels := guild.Channels
 | 
			
		||||
  if len(guildChannels) == 0 {
 | 
			
		||||
    c, err := session.GuildChannels(guildId)
 | 
			
		||||
 | 
			
		||||
  parsedGuildId := discord.GuildID(guildSnowflake)
 | 
			
		||||
 | 
			
		||||
  guild, err := client.GuildStore.Guild(parsedGuildId)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    return channels
 | 
			
		||||
  }
 | 
			
		||||
    guildChannels = c
 | 
			
		||||
 | 
			
		||||
  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 {
 | 
			
		||||
    return channels
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if withCategories {
 | 
			
		||||
    categories := make(map[string][]*discordgo.Channel)
 | 
			
		||||
    categories := make(map[string][]discord.Channel)
 | 
			
		||||
 | 
			
		||||
    for _, channel := range guildChannels {
 | 
			
		||||
      if channel.Type != discordgo.ChannelTypeGuildText && channel.Type != discordgo.ChannelTypeGuildNews {
 | 
			
		||||
      if channel.Type != discord.GuildText && channel.Type != discord.GuildAnnouncement {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID)
 | 
			
		||||
      if err != nil {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
      perms := discord.CalcOverwrites(*guild, channel, *selfMember)
 | 
			
		||||
 | 
			
		||||
      private := perms & discordgo.PermissionViewChannel == 0
 | 
			
		||||
      private := !perms.Has(discord.PermissionViewChannel)
 | 
			
		||||
      if private && !withPrivate {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      categoryID := "0"
 | 
			
		||||
      if channel.ParentID != "" {
 | 
			
		||||
        categoryID = channel.ParentID
 | 
			
		||||
      if channel.ParentID.IsValid() {
 | 
			
		||||
        categoryID = channel.ParentID.String()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      _, has := categories[categoryID]
 | 
			
		||||
      if !has {
 | 
			
		||||
        categories[categoryID] = make([]*discordgo.Channel, 0)
 | 
			
		||||
        categories[categoryID] = make([]discord.Channel, 0)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      categories[categoryID] = append(categories[categoryID], channel)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for id, channels := range categories {
 | 
			
		||||
      sort.Slice(channels, func(i, j int) bool {
 | 
			
		||||
        return channels[i].Position < channels[j].Position
 | 
			
		||||
    for id, category := range categories {
 | 
			
		||||
      // sort channels by 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" {
 | 
			
		||||
        for _, channel := range guild.Channels {
 | 
			
		||||
          if channel.ID == id {
 | 
			
		||||
        parsedCategoryId, err := discord.ParseSnowflake(id)
 | 
			
		||||
        if err != nil {
 | 
			
		||||
          continue
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for _, channel := range guildChannels {
 | 
			
		||||
          if channel.ID == discord.ChannelID(parsedCategoryId) {
 | 
			
		||||
            categoryChannels = append(categoryChannels, channel)
 | 
			
		||||
            break
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      for _, channel := range channels {
 | 
			
		||||
 | 
			
		||||
      // append channels
 | 
			
		||||
      for _, channel := range category {
 | 
			
		||||
        categoryChannels = append(categoryChannels, channel)
 | 
			
		||||
      }
 | 
			
		||||
      categories[id] = categoryChannels
 | 
			
		||||
| 
						 | 
				
			
			@ -137,12 +163,15 @@ func GetSortedChannels(session *discordgo.Session, guildId string, withCategorie
 | 
			
		|||
      keys = append(keys, id)
 | 
			
		||||
    }
 | 
			
		||||
    sort.Slice(keys, func(i, j int) bool {
 | 
			
		||||
      ca, _ := session.State.Channel(keys[i])
 | 
			
		||||
      cb, _ := session.State.Channel(keys[j])
 | 
			
		||||
      pa, _ := discord.ParseSnowflake(keys[i])
 | 
			
		||||
      pb, _ := discord.ParseSnowflake(keys[i])
 | 
			
		||||
 | 
			
		||||
      ca, _ := client.ChannelStore.Channel(discord.ChannelID(pa))
 | 
			
		||||
      cb, _ := client.ChannelStore.Channel(discord.ChannelID(pb))
 | 
			
		||||
 | 
			
		||||
      return ca.Position < cb.Position
 | 
			
		||||
    })
 | 
			
		||||
    sortedCategories := make(map[string][]*discordgo.Channel)
 | 
			
		||||
    sortedCategories := make(map[string][]discord.Channel)
 | 
			
		||||
    sortedCategories["0"] = categories["0"]
 | 
			
		||||
 | 
			
		||||
    for _, id := range keys {
 | 
			
		||||
| 
						 | 
				
			
			@ -156,16 +185,13 @@ func GetSortedChannels(session *discordgo.Session, guildId string, withCategorie
 | 
			
		|||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    for _, channel := range guildChannels {
 | 
			
		||||
      if channel.Type != discordgo.ChannelTypeGuildText && channel.Type != discordgo.ChannelTypeGuildNews {
 | 
			
		||||
      if channel.Type != discord.GuildText && channel.Type != discord.GuildAnnouncement {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID)
 | 
			
		||||
      if err != nil {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
      perms := discord.CalcOverwrites(*guild, channel, *selfMember)
 | 
			
		||||
 | 
			
		||||
      private := perms & discordgo.PermissionViewChannel == 0
 | 
			
		||||
      private := !perms.Has(discord.PermissionViewChannel)
 | 
			
		||||
      if private && !withPrivate {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -181,24 +207,47 @@ func GetSortedChannels(session *discordgo.Session, guildId string, withCategorie
 | 
			
		|||
  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()
 | 
			
		||||
  if currentGuild == "" {
 | 
			
		||||
    fmt.Print("<not in a guild>\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  longest := 0
 | 
			
		||||
  channels := GetSortedChannels(session, currentGuild, true, false)
 | 
			
		||||
 | 
			
		||||
  for _, channel := range channels {
 | 
			
		||||
    perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID)
 | 
			
		||||
  guildSnowflake, err := discord.ParseSnowflake(currentGuild)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
      continue
 | 
			
		||||
    fmt.Print("<failed to parse current guild id: ", err.Error(), ">\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    private := perms & discordgo.PermissionViewChannel == 0
 | 
			
		||||
    category := channel.Type == discordgo.ChannelTypeGuildCategory
 | 
			
		||||
  parsedGuildId := discord.GuildID(guildSnowflake)
 | 
			
		||||
  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
 | 
			
		||||
    if category {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,13 +269,10 @@ func ListChannelsCommand(session *discordgo.Session) {
 | 
			
		|||
  fmt.Printf("  %*s    created  topic\n\r", longest, "channel-name")
 | 
			
		||||
  fmt.Print(strings.Repeat("-", 80) + "\n\r")
 | 
			
		||||
  for _, channel := range channels {
 | 
			
		||||
    perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    perms := discord.CalcOverwrites(*guild, channel, *selfMember)
 | 
			
		||||
 | 
			
		||||
    private := perms & discordgo.PermissionViewChannel == 0
 | 
			
		||||
    category := channel.Type == discordgo.ChannelTypeGuildCategory
 | 
			
		||||
    private := !perms.Has(discord.PermissionViewChannel)
 | 
			
		||||
    category := channel.Type == discord.GuildCategory
 | 
			
		||||
    topic := REGEX_EMOTE.ReplaceAllString(channel.Topic, ":$1:")
 | 
			
		||||
    topic = strings.ReplaceAll(topic, "\n", " ")
 | 
			
		||||
    name := channel.Name
 | 
			
		||||
| 
						 | 
				
			
			@ -249,10 +295,8 @@ func ListChannelsCommand(session *discordgo.Session) {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    created := "??-???-??"
 | 
			
		||||
    timestamp, err := discordgo.SnowflakeTimestamp(channel.ID)
 | 
			
		||||
    if err == nil {
 | 
			
		||||
    timestamp := channel.CreatedAt()
 | 
			
		||||
    created = timestamp.UTC().Format("02-Jan-06")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fmt.Printf("  %*s  %s  %s\n\r", longest, name, created, topic)
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -263,11 +307,13 @@ func ListChannelsCommand(session *discordgo.Session) {
 | 
			
		|||
type ListedMember struct {
 | 
			
		||||
  Name string
 | 
			
		||||
  Bot bool
 | 
			
		||||
  Status discordgo.Status
 | 
			
		||||
  Status discord.Status
 | 
			
		||||
  Position int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ListUsersCommand(session *discordgo.Session) {
 | 
			
		||||
func ListUsersCommand() {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  currentGuild := state.GetCurrentGuild()
 | 
			
		||||
  currentChannel := state.GetCurrentChannel()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -280,37 +326,49 @@ func ListUsersCommand(session *discordgo.Session) {
 | 
			
		|||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  guild, err := session.State.Guild(state.GetCurrentGuild())
 | 
			
		||||
  parsedGuildId, err := discord.ParseSnowflake(currentGuild)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    fmt.Print("<failed to parse guild id: ", err.Error(), ">\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  channel, err := session.State.Channel(currentChannel)
 | 
			
		||||
  parsedChannelId, err := discord.ParseSnowflake(currentChannel)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    fmt.Print("<failed to parse channel id: ", err.Error(), ">\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fmt.Print("\n\r")
 | 
			
		||||
  fmt.Printf("[you are in '%s' in '#%s' among %d]\n\r", guild.Name, channel.Name, guild.MemberCount)
 | 
			
		||||
  fmt.Print("\n\r")
 | 
			
		||||
  guild, err := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
  sortedMembers := make([]ListedMember, 0)
 | 
			
		||||
 | 
			
		||||
  for _, presence := range guild.Presences {
 | 
			
		||||
    if presence.Status == discordgo.StatusOffline {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    perms, err := session.State.UserChannelPermissions(presence.User.ID, currentChannel)
 | 
			
		||||
  presences, err := client.Presences(guild.ID)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    fmt.Print("<failed to get presences: ", err.Error(), ">\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for _, presence := range presences {
 | 
			
		||||
    if presence.Status == discord.OfflineStatus {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    if perms & discordgo.PermissionViewChannel == 0 {
 | 
			
		||||
    member, err := client.MemberStore.Member(guild.ID, presence.User.ID)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    member, err := session.State.Member(currentGuild, presence.User.ID)
 | 
			
		||||
    if err != nil {
 | 
			
		||||
    perms := discord.CalcOverwrites(*guild, *channel, *member)
 | 
			
		||||
    if !perms.Has(discord.PermissionViewChannel) {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -320,8 +378,8 @@ func ListUsersCommand(session *discordgo.Session) {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    position := 0
 | 
			
		||||
    for _, id := range member.Roles {
 | 
			
		||||
      role, err := session.State.Role(currentGuild, id)
 | 
			
		||||
    for _, id := range member.RoleIDs {
 | 
			
		||||
      role, err := client.RoleStore.Role(guild.ID, id)
 | 
			
		||||
      if err != nil {
 | 
			
		||||
        continue
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -339,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)
 | 
			
		||||
  for _, member := range sortedMembers {
 | 
			
		||||
    _, has := membersByPosition[member.Position]
 | 
			
		||||
| 
						 | 
				
			
			@ -377,11 +439,11 @@ func ListUsersCommand(session *discordgo.Session) {
 | 
			
		|||
    for _, member := range members {
 | 
			
		||||
 | 
			
		||||
      statusColor := "reset"
 | 
			
		||||
      if member.Status == discordgo.StatusOnline {
 | 
			
		||||
      if member.Status == discord.OnlineStatus {
 | 
			
		||||
        statusColor = "green+b"
 | 
			
		||||
      } else if member.Status == discordgo.StatusIdle {
 | 
			
		||||
      } else if member.Status == discord.IdleStatus {
 | 
			
		||||
        statusColor = "yellow+b"
 | 
			
		||||
      } else if member.Status == discordgo.StatusDoNotDisturb {
 | 
			
		||||
      } else if member.Status == discord.DoNotDisturbStatus {
 | 
			
		||||
        statusColor = "red+b"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -425,16 +487,24 @@ func ListUsersCommand(session *discordgo.Session) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SwitchGuild(session *discordgo.Session, input string) {
 | 
			
		||||
func SwitchGuild(input string) {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  if input == "" {
 | 
			
		||||
    ListChannelsCommand(session)
 | 
			
		||||
    ListUsersCommand(session)
 | 
			
		||||
    ListChannelsCommand()
 | 
			
		||||
    ListUsersCommand()
 | 
			
		||||
  } else {
 | 
			
		||||
    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 {
 | 
			
		||||
        target = guild.ID
 | 
			
		||||
        target = guild.ID.String()
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -445,34 +515,33 @@ func SwitchGuild(session *discordgo.Session, input string) {
 | 
			
		|||
      state.SetCurrentGuild(target)
 | 
			
		||||
      last := state.GetLastChannel(target)
 | 
			
		||||
      if last == "" {
 | 
			
		||||
        channels := GetSortedChannels(session, target, false, false)
 | 
			
		||||
        // NB: ????????????????
 | 
			
		||||
        channels := GetSortedChannels(target, false, false)
 | 
			
		||||
        if len(channels) > 0 {
 | 
			
		||||
          topChannel := channels[0]
 | 
			
		||||
 | 
			
		||||
          state.SetCurrentChannel(topChannel.ID)
 | 
			
		||||
          state.SetLastChannel(target, topChannel.ID)
 | 
			
		||||
          state.SetCurrentChannel(topChannel.ID.String())
 | 
			
		||||
          state.SetLastChannel(target, topChannel.ID.String())
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        state.SetCurrentChannel(last)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ListChannelsCommand(session)
 | 
			
		||||
      ListUsersCommand(session)
 | 
			
		||||
      ListChannelsCommand()
 | 
			
		||||
      ListUsersCommand()
 | 
			
		||||
 | 
			
		||||
      lib.UpdatePresence(session)
 | 
			
		||||
      lib.UpdatePresence()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SwitchGuildsCommand(session *discordgo.Session) {
 | 
			
		||||
  lib.MakePrompt(session, ":guild> ", false, func(session *discordgo.Session, input string, interrupt bool) {
 | 
			
		||||
func SwitchGuildsCommand() {
 | 
			
		||||
  lib.MakePrompt(":guild> ", false, func(input string, interrupt bool) {
 | 
			
		||||
    fmt.Print("\r")
 | 
			
		||||
    SwitchGuild(session, input)
 | 
			
		||||
    SwitchGuild(input)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SwitchChannelsCommand(session *discordgo.Session) {
 | 
			
		||||
func SwitchChannelsCommand() {
 | 
			
		||||
  currentGuild := state.GetCurrentGuild()
 | 
			
		||||
 | 
			
		||||
  if currentGuild == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -480,18 +549,18 @@ func SwitchChannelsCommand(session *discordgo.Session) {
 | 
			
		|||
    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")
 | 
			
		||||
    if input == "" {
 | 
			
		||||
      ListUsersCommand(session)
 | 
			
		||||
      ListUsersCommand()
 | 
			
		||||
    } else {
 | 
			
		||||
      target := ""
 | 
			
		||||
 | 
			
		||||
      channels := GetSortedChannels(session, currentGuild, false, false)
 | 
			
		||||
      channels := GetSortedChannels(currentGuild, false, false)
 | 
			
		||||
 | 
			
		||||
      for _, channel := range channels {
 | 
			
		||||
        if strings.Index(strings.ToLower(channel.Name), strings.ToLower(input)) > -1 {
 | 
			
		||||
          target = channel.ID
 | 
			
		||||
          target = channel.ID.String()
 | 
			
		||||
          break
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -502,9 +571,9 @@ func SwitchChannelsCommand(session *discordgo.Session) {
 | 
			
		|||
        state.SetCurrentChannel(target)
 | 
			
		||||
        state.SetLastChannel(currentGuild, target)
 | 
			
		||||
 | 
			
		||||
        ListUsersCommand(session)
 | 
			
		||||
        ListUsersCommand()
 | 
			
		||||
 | 
			
		||||
        lib.UpdatePresence(session)
 | 
			
		||||
        lib.UpdatePresence()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@ import (
 | 
			
		|||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/mgutz/ansi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +42,7 @@ func lessLower(sa, sb string) bool {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HelpCommand(session *discordgo.Session) {
 | 
			
		||||
func HelpCommand() {
 | 
			
		||||
  noColor := state.HasNoColor()
 | 
			
		||||
 | 
			
		||||
  fmt.Println("\r\nCOMcord (c)left 2023\n\r")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,11 +8,19 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Cynosphere/comcord/lib"
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/discord"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func GetHistory(session *discordgo.Session, limit int, channel string) {
 | 
			
		||||
  messages, err := session.ChannelMessages(channel, 100, "", "", "")
 | 
			
		||||
func GetHistory(limit int, channel string) {
 | 
			
		||||
  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 {
 | 
			
		||||
    fmt.Print("<failed to get messages: ", err.Error(), ">\n\r")
 | 
			
		||||
    return
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +34,7 @@ func GetHistory(session *discordgo.Session, limit int, channel string) {
 | 
			
		|||
 | 
			
		||||
  lines := make([]string, 0)
 | 
			
		||||
  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 {
 | 
			
		||||
      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")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HistoryCommand(session *discordgo.Session) {
 | 
			
		||||
func HistoryCommand() {
 | 
			
		||||
  currentChannel := state.GetCurrentChannel()
 | 
			
		||||
  if currentChannel == "" {
 | 
			
		||||
    fmt.Print("<not in a channel>\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetHistory(session, 20, currentChannel)
 | 
			
		||||
  GetHistory(20, currentChannel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExtendedHistoryCommand(session *discordgo.Session) {
 | 
			
		||||
func ExtendedHistoryCommand() {
 | 
			
		||||
  currentChannel := state.GetCurrentChannel()
 | 
			
		||||
  if currentChannel == "" {
 | 
			
		||||
    fmt.Print("<not in a channel>\n\r")
 | 
			
		||||
    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")
 | 
			
		||||
    limit, err := strconv.Atoi(input)
 | 
			
		||||
 | 
			
		||||
    if err != nil {
 | 
			
		||||
      fmt.Print("<not a number>\n\r")
 | 
			
		||||
    } else {
 | 
			
		||||
      GetHistory(session, limit, currentChannel)
 | 
			
		||||
      GetHistory(limit, currentChannel)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PeekHistory(session *discordgo.Session, guild, channel string) {
 | 
			
		||||
func PeekHistory(guild, channel string) {
 | 
			
		||||
  target := ""
 | 
			
		||||
 | 
			
		||||
  channels := GetSortedChannels(session, guild, false, false)
 | 
			
		||||
  channels := GetSortedChannels(guild, false, false)
 | 
			
		||||
 | 
			
		||||
  for _, c := range channels {
 | 
			
		||||
    if strings.Index(strings.ToLower(c.Name), strings.ToLower(channel)) > -1 {
 | 
			
		||||
      target = c.ID
 | 
			
		||||
      target = c.ID.String()
 | 
			
		||||
      break
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -85,36 +93,44 @@ func PeekHistory(session *discordgo.Session, guild, channel string) {
 | 
			
		|||
  if target == "" {
 | 
			
		||||
    fmt.Print("<channel not found>\n\r")
 | 
			
		||||
  } else {
 | 
			
		||||
    GetHistory(session, 20, target)
 | 
			
		||||
    GetHistory(20, target)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PeekCommand(session *discordgo.Session) {
 | 
			
		||||
func PeekCommand() {
 | 
			
		||||
  currentGuild := state.GetCurrentGuild()
 | 
			
		||||
  if currentGuild == "" {
 | 
			
		||||
    fmt.Print("<not in a guild>\n\r")
 | 
			
		||||
    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")
 | 
			
		||||
 | 
			
		||||
    if input != "" {
 | 
			
		||||
      PeekHistory(session, currentGuild, input)
 | 
			
		||||
      PeekHistory(currentGuild, input)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CrossPeekCommand(session *discordgo.Session) {
 | 
			
		||||
  lib.MakePrompt(session, ":guild> ", false, func(session *discordgo.Session, input string, interrupt bool) {
 | 
			
		||||
func CrossPeekCommand() {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  lib.MakePrompt(":guild> ", false, func(input string, interrupt bool) {
 | 
			
		||||
    fmt.Print("\r")
 | 
			
		||||
 | 
			
		||||
    if input != "" {
 | 
			
		||||
      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 {
 | 
			
		||||
          targetGuild = guild.ID
 | 
			
		||||
          targetGuild = guild.ID.String()
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -122,11 +138,11 @@ func CrossPeekCommand(session *discordgo.Session) {
 | 
			
		|||
      if targetGuild == "" {
 | 
			
		||||
        fmt.Print("<guild not found>\n\r")
 | 
			
		||||
      } 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")
 | 
			
		||||
 | 
			
		||||
          if input != "" {
 | 
			
		||||
            PeekHistory(session, targetGuild, input)
 | 
			
		||||
            PeekHistory(targetGuild, input)
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,9 @@
 | 
			
		|||
package commands
 | 
			
		||||
 | 
			
		||||
import "github.com/bwmarrin/discordgo"
 | 
			
		||||
 | 
			
		||||
var commandMap map[string]Command
 | 
			
		||||
 | 
			
		||||
type Command struct {
 | 
			
		||||
  Run func(*discordgo.Session)
 | 
			
		||||
  Run func()
 | 
			
		||||
  Description string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,11 +4,13 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
	"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")
 | 
			
		||||
  session.Close()
 | 
			
		||||
  client.Close()
 | 
			
		||||
  os.Exit(0)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,43 +7,64 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Cynosphere/comcord/lib"
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/discord"
 | 
			
		||||
	"github.com/mgutz/ansi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func SendMode(session *discordgo.Session) {
 | 
			
		||||
func SendMode() {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  channelId := state.GetCurrentChannel()
 | 
			
		||||
  if channelId == "" {
 | 
			
		||||
    fmt.Print("<not in a channel>\n\r")
 | 
			
		||||
    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 {
 | 
			
		||||
    fmt.Print("<error getting channel: ", err.Error(), ">\n\r")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  perms, err := session.State.UserChannelPermissions(session.State.User.ID, channel.ID)
 | 
			
		||||
  guild, err := client.GuildStore.Guild(channel.GuildID)
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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")
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  length := utf8.RuneCountInString(session.State.User.Username) + 2
 | 
			
		||||
  length := utf8.RuneCountInString(self.Username) + 2
 | 
			
		||||
  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() {
 | 
			
		||||
    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 interrupt {
 | 
			
		||||
        fmt.Print("^C<no message sent>\n\r")
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +73,7 @@ func SendMode(session *discordgo.Session) {
 | 
			
		|||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      fmt.Print(prompt, input, "\n\r")
 | 
			
		||||
      _, err := session.ChannelMessageSend(channelId, input)
 | 
			
		||||
      _, err := client.SendMessage(channel.ID, input)
 | 
			
		||||
 | 
			
		||||
      if err != nil {
 | 
			
		||||
        fmt.Print("<failed to send message: ", err, ">\n\r")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,11 +3,9 @@ package commands
 | 
			
		|||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TimeCommand(session *discordgo.Session) {
 | 
			
		||||
func TimeCommand() {
 | 
			
		||||
  now := time.Now().UTC()
 | 
			
		||||
 | 
			
		||||
  fmt.Printf("%s\n\r", now.Format("[Mon 02-Jan-06 15:04:05]"))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,11 @@
 | 
			
		|||
package events
 | 
			
		||||
 | 
			
		||||
import "github.com/bwmarrin/discordgo"
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/diamondburned/ningen/v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Setup(session *discordgo.Session) {
 | 
			
		||||
  session.AddHandlerOnce(Ready)
 | 
			
		||||
  session.AddHandler(MessageCreate)
 | 
			
		||||
  session.AddHandler(MessageUpdate)
 | 
			
		||||
func Setup(session *ningen.State) {
 | 
			
		||||
  session.PreHandler.AddHandler(Ready)
 | 
			
		||||
  session.PreHandler.AddHandler(MessageCreate)
 | 
			
		||||
  session.PreHandler.AddHandler(MessageUpdate)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,65 +5,84 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Cynosphere/comcord/lib"
 | 
			
		||||
	"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) {
 | 
			
		||||
  if msg.Author.ID == session.State.User.ID {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  channel, err := session.State.Channel(msg.ChannelID)
 | 
			
		||||
func MessageCreate(msg *gateway.MessageCreateEvent) {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
  self, err := client.MeStore.Me()
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    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() {
 | 
			
		||||
    state.AddMessageToQueue(msg.Message)
 | 
			
		||||
  } 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 {
 | 
			
		||||
      fmt.Print(line)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if isDM {
 | 
			
		||||
    state.SetLastDM(msg.ChannelID)
 | 
			
		||||
    state.SetLastDM(msg.ChannelID.String())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func MessageUpdate(session *discordgo.Session, msg *discordgo.MessageUpdate) {
 | 
			
		||||
  if msg.Author == nil {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
func MessageUpdate(msg *gateway.MessageUpdateEvent) {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
  self, err := client.MeStore.Me()
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    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() {
 | 
			
		||||
    state.AddMessageToQueue(msg.Message)
 | 
			
		||||
  } 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 {
 | 
			
		||||
      fmt.Print(line)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if isDM {
 | 
			
		||||
    state.SetLastDM(msg.ChannelID)
 | 
			
		||||
    state.SetLastDM(msg.ChannelID.String())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,27 +6,41 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/Cynosphere/comcord/commands"
 | 
			
		||||
	"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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Ready(session *discordgo.Session, event *discordgo.Ready) {
 | 
			
		||||
  fmt.Printf("\rLogged in as: %s\n\r", ansi.Color(fmt.Sprintf("%s (%s)", session.State.User.Username, session.State.User.ID), "yellow"))
 | 
			
		||||
func Ready(event *gateway.ReadyEvent) {
 | 
			
		||||
  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")
 | 
			
		||||
  defaultChannel := state.GetConfigValue("defaultChannel")
 | 
			
		||||
  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 defaultChannel != "" {
 | 
			
		||||
        state.SetCurrentChannel(defaultChannel)
 | 
			
		||||
        state.SetLastChannel(defaultGuild, defaultChannel)
 | 
			
		||||
      }
 | 
			
		||||
      commands.SwitchGuild(session, guild.Name)
 | 
			
		||||
      commands.SwitchGuild(guild.Name)
 | 
			
		||||
    } else {
 | 
			
		||||
      fmt.Println("\r% This account is not in the defined default guild.")
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								go.mod
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -4,18 +4,24 @@ go 1.20
 | 
			
		|||
 | 
			
		||||
require (
 | 
			
		||||
	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/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/gorilla/schema v1.2.0 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.4.2 // 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-isatty v0.0.17 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.13 // 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
 | 
			
		||||
	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/term v0.10.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.9.0 // indirect
 | 
			
		||||
	golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										246
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										246
									
								
								go.sum
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,5 +1,24 @@
 | 
			
		|||
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
 | 
			
		||||
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.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
 | 
			
		||||
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/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
 | 
			
		||||
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/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/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/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/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.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/go.mod h1:8RNv74chpO0eTm6rdD1H6WZGihL5rJ+RfSlhv4fIfjg=
 | 
			
		||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
 | 
			
		||||
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.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/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.10/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/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
 | 
			
		||||
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/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.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
 | 
			
		||||
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 | 
			
		||||
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/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.6.1/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/go.mod h1:Zgz8IJWvJoe7NK23CCPpC109XMCqJCpUhpHcnnA4XaM=
 | 
			
		||||
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/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-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/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-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-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-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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
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.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-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.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/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.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
			
		||||
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/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-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 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/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.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-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=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ import (
 | 
			
		|||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/discord"
 | 
			
		||||
	"github.com/mgutz/ansi"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,12 +19,12 @@ var REGEX_EMOTE = regexp.MustCompile(`<(?:\x{200b}|&)?a?:(\w+):(\d+)>`)
 | 
			
		|||
type MessageOptions struct {
 | 
			
		||||
  Content string
 | 
			
		||||
  Name string
 | 
			
		||||
  Channel string
 | 
			
		||||
  Channel discord.ChannelID
 | 
			
		||||
  Bot bool
 | 
			
		||||
  Webhook bool
 | 
			
		||||
  Attachments []*discordgo.MessageAttachment
 | 
			
		||||
  Stickers []*discordgo.Sticker
 | 
			
		||||
  Reply *discordgo.Message
 | 
			
		||||
  Attachments []discord.Attachment
 | 
			
		||||
  Stickers []discord.StickerItem
 | 
			
		||||
  Reply *discord.Message
 | 
			
		||||
  Timestamp time.Time
 | 
			
		||||
  IsMention bool
 | 
			
		||||
  IsDM bool
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +35,9 @@ type MessageOptions struct {
 | 
			
		|||
  InHistory bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FormatMessage(session *discordgo.Session, options MessageOptions) []string {
 | 
			
		||||
func FormatMessage(options MessageOptions) []string {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  lines := make([]string, 0)
 | 
			
		||||
 | 
			
		||||
  timestamp := options.Timestamp.UTC().Format("[15:04:05]")
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +57,7 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
 | 
			
		|||
 | 
			
		||||
    headerLength := 6 + utf8.RuneCountInString(options.Reply.Author.Username)
 | 
			
		||||
 | 
			
		||||
    content, _ := options.Reply.ContentWithMoreMentionsReplaced(session)
 | 
			
		||||
    content := options.Reply.Content
 | 
			
		||||
    replyContent := strings.ReplaceAll(content, "\n", " ")
 | 
			
		||||
 | 
			
		||||
    // 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))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stickerCount := len(options.Reply.StickerItems)
 | 
			
		||||
    stickerCount := len(options.Reply.Stickers)
 | 
			
		||||
    if stickerCount > 0 {
 | 
			
		||||
      stickerPlural := ""
 | 
			
		||||
      if stickerCount > 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -161,11 +163,11 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
 | 
			
		|||
 | 
			
		||||
      lines = append(lines, str + "\n\r")
 | 
			
		||||
    } else if options.IsJoin {
 | 
			
		||||
      channel, err := session.State.Channel(options.Channel)
 | 
			
		||||
      channel, err := client.ChannelStore.Channel(options.Channel)
 | 
			
		||||
      if err != nil {
 | 
			
		||||
        return lines
 | 
			
		||||
      }
 | 
			
		||||
      guild, err := session.State.Guild(channel.GuildID)
 | 
			
		||||
      guild, err := client.GuildStore.Guild(channel.GuildID)
 | 
			
		||||
      if err != nil {
 | 
			
		||||
        return lines
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -237,27 +239,33 @@ func FormatMessage(session *discordgo.Session, options MessageOptions) []string
 | 
			
		|||
  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)
 | 
			
		||||
 | 
			
		||||
  channel, err := session.State.Channel(msg.ChannelID)
 | 
			
		||||
  channel, err := client.ChannelStore.Channel(msg.ChannelID)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    return lines
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  guild, err := session.State.Guild(channel.GuildID)
 | 
			
		||||
  guild, err := client.GuildStore.Guild(channel.GuildID)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    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 {
 | 
			
		||||
    return lines
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hasMentionedRole := false
 | 
			
		||||
  for _, role := range msg.MentionRoles {
 | 
			
		||||
    for _, selfRole := range selfMember.Roles {
 | 
			
		||||
  for _, role := range msg.MentionRoleIDs {
 | 
			
		||||
    for _, selfRole := range selfMember.RoleIDs {
 | 
			
		||||
      if role == selfRole {
 | 
			
		||||
        hasMentionedRole = true
 | 
			
		||||
        break;
 | 
			
		||||
| 
						 | 
				
			
			@ -267,18 +275,18 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
 | 
			
		|||
 | 
			
		||||
  isDirectlyMentioned := false
 | 
			
		||||
  for _, user := range msg.Mentions {
 | 
			
		||||
    if user.ID == session.State.User.ID {
 | 
			
		||||
    if user.ID == self.ID {
 | 
			
		||||
      isDirectlyMentioned = true
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isPing := msg.MentionEveryone || hasMentionedRole || isDirectlyMentioned
 | 
			
		||||
  isDM := channel.Type == discordgo.ChannelTypeDM || channel.Type == discordgo.ChannelTypeGroupDM
 | 
			
		||||
  isEdit := msg.EditedTimestamp != nil
 | 
			
		||||
  isDM := channel.Type == discord.DirectMessage || channel.Type == discord.GroupDM
 | 
			
		||||
  isEdit := msg.EditedTimestamp.IsValid()
 | 
			
		||||
 | 
			
		||||
  currentChannel := state.GetCurrentChannel()
 | 
			
		||||
  isCurrentChannel := currentChannel == msg.ChannelID
 | 
			
		||||
  isCurrentChannel := currentChannel == msg.ChannelID.String()
 | 
			
		||||
 | 
			
		||||
  if !isCurrentChannel && !isDM && !isPing && !options.InHistory {
 | 
			
		||||
    return lines
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +300,7 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
 | 
			
		|||
    str = str + "\x07\n\r"
 | 
			
		||||
    lines = append(lines, str)
 | 
			
		||||
  } else {
 | 
			
		||||
    content, _ := msg.ContentWithMoreMentionsReplaced(session)
 | 
			
		||||
    content := msg.Content
 | 
			
		||||
    if isEdit {
 | 
			
		||||
      content = content + " (edited)"
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -305,19 +313,19 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
 | 
			
		|||
        options.Name = msg.Author.Username
 | 
			
		||||
        options.Channel = msg.ChannelID
 | 
			
		||||
        options.Bot = msg.Author.Bot
 | 
			
		||||
        options.Webhook = msg.WebhookID != ""
 | 
			
		||||
        options.Webhook = msg.WebhookID.IsValid()
 | 
			
		||||
        options.Attachments = msg.Attachments
 | 
			
		||||
        options.Stickers = msg.StickerItems
 | 
			
		||||
        options.Stickers = msg.Stickers
 | 
			
		||||
        if i == 0 {
 | 
			
		||||
          options.Reply = msg.ReferencedMessage
 | 
			
		||||
        }
 | 
			
		||||
        options.IsMention = isPing
 | 
			
		||||
        options.IsDM = isDM
 | 
			
		||||
        options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin
 | 
			
		||||
        options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage
 | 
			
		||||
        options.IsJoin = msg.Type == discord.GuildMemberJoinMessage
 | 
			
		||||
        options.IsPin = msg.Type == discord.ChannelPinnedMessage
 | 
			
		||||
        options.IsDump = false
 | 
			
		||||
 | 
			
		||||
        msgLines := FormatMessage(session, options)
 | 
			
		||||
        msgLines := FormatMessage(options)
 | 
			
		||||
        for _, line := range msgLines {
 | 
			
		||||
          lines = append(lines, line)
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -327,28 +335,28 @@ func ProcessMessage(session *discordgo.Session, msg *discordgo.Message, options
 | 
			
		|||
      options.Name = msg.Author.Username
 | 
			
		||||
      options.Channel = msg.ChannelID
 | 
			
		||||
      options.Bot = msg.Author.Bot
 | 
			
		||||
      options.Webhook = msg.WebhookID != ""
 | 
			
		||||
      options.Webhook = msg.WebhookID.IsValid()
 | 
			
		||||
      options.Attachments = msg.Attachments
 | 
			
		||||
      options.Stickers = msg.StickerItems
 | 
			
		||||
      options.Stickers = msg.Stickers
 | 
			
		||||
      options.Reply = msg.ReferencedMessage
 | 
			
		||||
      options.IsMention = isPing
 | 
			
		||||
      options.IsDM = isDM
 | 
			
		||||
      options.IsJoin = msg.Type == discordgo.MessageTypeGuildMemberJoin
 | 
			
		||||
      options.IsPin = msg.Type == discordgo.MessageTypeChannelPinnedMessage
 | 
			
		||||
      options.IsJoin = msg.Type == discord.GuildMemberJoinMessage
 | 
			
		||||
      options.IsPin = msg.Type == discord.ChannelPinnedMessage
 | 
			
		||||
      options.IsDump = isDump
 | 
			
		||||
 | 
			
		||||
      lines = FormatMessage(session, options)
 | 
			
		||||
      lines = FormatMessage(options)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return lines
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ProcessQueue(session *discordgo.Session) {
 | 
			
		||||
func ProcessQueue() {
 | 
			
		||||
  queue := state.GetMessageQueue()
 | 
			
		||||
 | 
			
		||||
  for _, msg := range queue {
 | 
			
		||||
    lines := ProcessMessage(session, msg, MessageOptions{NoColor: state.HasNoColor()})
 | 
			
		||||
    lines := ProcessMessage(msg, MessageOptions{NoColor: state.HasNoColor()})
 | 
			
		||||
    for _, line := range lines {
 | 
			
		||||
      fmt.Print(line)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										149
									
								
								lib/presence.go
									
										
									
									
									
								
							
							
						
						
									
										149
									
								
								lib/presence.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,98 +1,57 @@
 | 
			
		|||
package lib
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/discord"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/gateway"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ActivityMetadata struct {
 | 
			
		||||
  ButtonURLs []string `json:"button_urls,omitempty"`
 | 
			
		||||
func UpdatePresence() {
 | 
			
		||||
  client := state.GetClient()
 | 
			
		||||
 | 
			
		||||
  self, err := client.MeStore.Me()
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
type Activity struct {
 | 
			
		||||
  Name          string                 `json:"name"`
 | 
			
		||||
	Type          discordgo.ActivityType `json:"type"`
 | 
			
		||||
	//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()
 | 
			
		||||
  presence := GatewayPresenceUpdate{
 | 
			
		||||
  presence := gateway.UpdatePresenceCommand{
 | 
			
		||||
    Since: 0,
 | 
			
		||||
    AFK: afk,
 | 
			
		||||
    Activities: make([]Activity, 0),
 | 
			
		||||
    Activities: make([]discord.Activity, 0),
 | 
			
		||||
    AFK: false,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  currentGuild := state.GetCurrentGuild()
 | 
			
		||||
  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()
 | 
			
		||||
 | 
			
		||||
  if session.State.User.Bot {
 | 
			
		||||
    activity = Activity{
 | 
			
		||||
      Type: 0,
 | 
			
		||||
  if self.Bot {
 | 
			
		||||
    activity = discord.Activity{
 | 
			
		||||
      Type: discord.GameActivity,
 | 
			
		||||
      Name: "comcord",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if currentGuild != "" && currentChannel != "" {
 | 
			
		||||
      guild, guildErr := session.State.Guild(currentGuild)
 | 
			
		||||
      channel, channelErr := session.State.Channel(currentChannel)
 | 
			
		||||
      guild, guildErr := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
 | 
			
		||||
      channel, channelErr := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
 | 
			
		||||
 | 
			
		||||
      if guildErr == nil && channelErr == nil {
 | 
			
		||||
        activity.Type = 3
 | 
			
		||||
        activity.Type = discord.WatchingActivity
 | 
			
		||||
        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]"
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    activity = Activity{
 | 
			
		||||
      Type: 0,
 | 
			
		||||
      ApplicationID: "1026163285877325874",
 | 
			
		||||
      Name: "comcord",
 | 
			
		||||
      Timestamps: discordgo.TimeStamps{
 | 
			
		||||
        StartTimestamp: startTime.Unix(),
 | 
			
		||||
      },
 | 
			
		||||
      Buttons: make([]string, 0),
 | 
			
		||||
      Metadata: ActivityMetadata{
 | 
			
		||||
        ButtonURLs: make([]string, 0),
 | 
			
		||||
      },
 | 
			
		||||
    parsedAppId, err := discord.ParseSnowflake("1026163285877325874")
 | 
			
		||||
    if err != nil {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    activity.Buttons = append(activity.Buttons, "comcord Repo")
 | 
			
		||||
    activity.Metadata.ButtonURLs = append(activity.Metadata.ButtonURLs, "https://gitdab.com/Cynosphere/comcord")
 | 
			
		||||
    activity = discord.Activity{
 | 
			
		||||
      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 != "" {
 | 
			
		||||
      guild, guildErr := session.State.Guild(currentGuild)
 | 
			
		||||
      channel, channelErr := session.State.Channel(currentChannel)
 | 
			
		||||
      guild, guildErr := client.GuildStore.Guild(discord.GuildID(parsedGuildId))
 | 
			
		||||
      channel, channelErr := client.ChannelStore.Channel(discord.ChannelID(parsedChannelId))
 | 
			
		||||
 | 
			
		||||
      if guildErr == nil && channelErr == nil {
 | 
			
		||||
        activity.Details = fmt.Sprintf("#%s - %s", channel.Name, guild.Name)
 | 
			
		||||
 | 
			
		||||
        activity.Assets = discordgo.Assets{}
 | 
			
		||||
        activity.Assets = &discord.ActivityAssets{}
 | 
			
		||||
        activity.Assets.LargeText = guild.Name
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
  defaultStatus := state.GetConfigValue("defaultStatus")
 | 
			
		||||
  if defaultStatus != "" {
 | 
			
		||||
    presence.Status = defaultStatus
 | 
			
		||||
    presence.Status = discord.Status(defaultStatus)
 | 
			
		||||
  } else {
 | 
			
		||||
    if afk {
 | 
			
		||||
      presence.Status = "idle"
 | 
			
		||||
      presence.Status = discord.IdleStatus
 | 
			
		||||
    } else {
 | 
			
		||||
      presence.Status = "online"
 | 
			
		||||
      presence.Status = discord.OnlineStatus
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  op := presenceOp{3, presence}
 | 
			
		||||
  wsMutex.Lock()
 | 
			
		||||
  wsConn.WriteJSON(op)
 | 
			
		||||
  wsMutex.Unlock()
 | 
			
		||||
  client.Gateway().Send(context.Background(), &presence)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,11 +4,10 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/Cynosphere/comcord/state"
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"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.SetPromptText(prompt)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,10 +23,10 @@ func MakePrompt(session *discordgo.Session, prompt string, uniqueLine bool, call
 | 
			
		|||
 | 
			
		||||
  interrupt := err == readline.ErrInterrupt
 | 
			
		||||
 | 
			
		||||
  callback(session, input, interrupt)
 | 
			
		||||
  callback(input, interrupt)
 | 
			
		||||
 | 
			
		||||
  state.SetInPrompt(false)
 | 
			
		||||
  state.SetPromptText("")
 | 
			
		||||
 | 
			
		||||
  ProcessQueue(session)
 | 
			
		||||
  ProcessQueue()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										80
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										80
									
								
								main.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"runtime"
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +13,10 @@ import (
 | 
			
		|||
	"github.com/Cynosphere/comcord/events"
 | 
			
		||||
	"github.com/Cynosphere/comcord/rcfile"
 | 
			
		||||
	"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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +64,6 @@ func main() {
 | 
			
		|||
  fmt.Println("\rType 'h' for Commands")
 | 
			
		||||
  fmt.Print("\r")
 | 
			
		||||
 | 
			
		||||
  state.Setup(config)
 | 
			
		||||
  commands.Setup()
 | 
			
		||||
 | 
			
		||||
  allowUserAccounts := config["allowUserAccounts"] == "true"
 | 
			
		||||
| 
						 | 
				
			
			@ -71,31 +74,26 @@ func main() {
 | 
			
		|||
 | 
			
		||||
  fullToken := tokenPrefix + token
 | 
			
		||||
 | 
			
		||||
  client, err := discordgo.New(fullToken)
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    fmt.Println("% Failed to create client:", err)
 | 
			
		||||
    fmt.Print("\r")
 | 
			
		||||
    os.Exit(1)
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //client.LogLevel = -1
 | 
			
		||||
  client.LogLevel = discordgo.LogDebug
 | 
			
		||||
 | 
			
		||||
  client.Identify.Intents = discordgo.IntentsAll
 | 
			
		||||
 | 
			
		||||
  client.Identify.Properties = discordgo.IdentifyProperties{
 | 
			
		||||
  props := gateway.IdentifyProperties{
 | 
			
		||||
    OS: runtime.GOOS,
 | 
			
		||||
  }
 | 
			
		||||
  statusType := config["statusType"]
 | 
			
		||||
  if statusType == "mobile" {
 | 
			
		||||
    client.Identify.Properties.Browser = "Discord Android"
 | 
			
		||||
    props.Browser = "Discord Android"
 | 
			
		||||
  } else if statusType == "embedded" {
 | 
			
		||||
    client.Identify.Properties.Browser = "Discord Embedded"
 | 
			
		||||
    props.Browser = "Discord Embedded"
 | 
			
		||||
  } else if statusType == "desktop" {
 | 
			
		||||
    client.Identify.Properties.Browser = "Discord Client"
 | 
			
		||||
    props.Browser = "Discord Client"
 | 
			
		||||
  } else {
 | 
			
		||||
    client.Identify.Properties.Browser = "comcord"
 | 
			
		||||
    props.Browser = "comcord"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ident := gateway.IdentifyCommand{
 | 
			
		||||
    Token: fullToken,
 | 
			
		||||
    Properties: props,
 | 
			
		||||
 | 
			
		||||
    Compress: true,
 | 
			
		||||
    LargeThreshold: 50,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  status := "online"
 | 
			
		||||
| 
						 | 
				
			
			@ -105,39 +103,57 @@ func main() {
 | 
			
		|||
  }
 | 
			
		||||
  startTime := state.GetStartTime()
 | 
			
		||||
 | 
			
		||||
  client.Identify.Presence = discordgo.GatewayStatusUpdate{
 | 
			
		||||
    Since: 0,
 | 
			
		||||
    Status: status,
 | 
			
		||||
    AFK: false,
 | 
			
		||||
    Game: discordgo.Activity{
 | 
			
		||||
      Type: 0,
 | 
			
		||||
  activity := discord.Activity{
 | 
			
		||||
    Name: "comcord",
 | 
			
		||||
      ApplicationID: "1026163285877325874",
 | 
			
		||||
      CreatedAt: startTime,
 | 
			
		||||
    Type: discord.GameActivity,
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
  err = client.Open()
 | 
			
		||||
  err = client.Open(context.Background())
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    fmt.Println("% Failed to connect to Discord:", err)
 | 
			
		||||
    fmt.Print("\r")
 | 
			
		||||
    os.Exit(1)
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  defer client.Close()
 | 
			
		||||
 | 
			
		||||
  keyboard.Listen(func(key keys.Key) (stop bool, err error) {
 | 
			
		||||
    if !state.IsInPrompt() {
 | 
			
		||||
      if key.Code == keys.CtrlC {
 | 
			
		||||
        commands.QuitCommand(client)
 | 
			
		||||
        commands.QuitCommand()
 | 
			
		||||
        return true, nil
 | 
			
		||||
      } else {
 | 
			
		||||
        command, has := commands.GetCommand(key.String())
 | 
			
		||||
        if has {
 | 
			
		||||
          command.Run(client)
 | 
			
		||||
          command.Run()
 | 
			
		||||
        } else {
 | 
			
		||||
          commands.SendMode(client)
 | 
			
		||||
          commands.SendMode()
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,11 +3,14 @@ package state
 | 
			
		|||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/bwmarrin/discordgo"
 | 
			
		||||
	"github.com/diamondburned/arikawa/v3/discord"
 | 
			
		||||
	"github.com/diamondburned/ningen/v3"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ComcordState struct {
 | 
			
		||||
  Client *ningen.State
 | 
			
		||||
  Config map[string]string
 | 
			
		||||
  Readied bool
 | 
			
		||||
  Connected bool
 | 
			
		||||
  RPCConnected bool
 | 
			
		||||
  StartTime time.Time
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +20,7 @@ type ComcordState struct {
 | 
			
		|||
  InPrompt bool
 | 
			
		||||
  PromptText string
 | 
			
		||||
  AFK bool
 | 
			
		||||
  MessageQueue []*discordgo.Message
 | 
			
		||||
  MessageQueue []discord.Message
 | 
			
		||||
  LastChannel map[string]string
 | 
			
		||||
  LastDM string
 | 
			
		||||
  NoColor bool
 | 
			
		||||
| 
						 | 
				
			
			@ -25,9 +28,11 @@ type ComcordState struct {
 | 
			
		|||
 | 
			
		||||
var state ComcordState
 | 
			
		||||
 | 
			
		||||
func Setup(config map[string]string) {
 | 
			
		||||
func Setup(config map[string]string, client *ningen.State) {
 | 
			
		||||
  state = ComcordState{}
 | 
			
		||||
  state.Client = client
 | 
			
		||||
  state.Config = config
 | 
			
		||||
  state.Readied = false
 | 
			
		||||
  state.Connected = true
 | 
			
		||||
  state.RPCConnected = false
 | 
			
		||||
  state.StartTime = time.Now()
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +42,24 @@ func Setup(config map[string]string) {
 | 
			
		|||
  state.InPrompt = false
 | 
			
		||||
  state.PromptText = ""
 | 
			
		||||
  state.AFK = false
 | 
			
		||||
  state.MessageQueue = make([]*discordgo.Message, 0)
 | 
			
		||||
  state.MessageQueue = make([]discord.Message, 0)
 | 
			
		||||
  state.LastChannel = make(map[string]string)
 | 
			
		||||
  state.LastDM = ""
 | 
			
		||||
  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 {
 | 
			
		||||
  return state.Connected
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -111,16 +128,16 @@ func SetAFK(value bool) {
 | 
			
		|||
  state.AFK = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetMessageQueue() []*discordgo.Message {
 | 
			
		||||
func GetMessageQueue() []discord.Message {
 | 
			
		||||
  return state.MessageQueue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AddMessageToQueue(msg *discordgo.Message) {
 | 
			
		||||
func AddMessageToQueue(msg discord.Message) {
 | 
			
		||||
  state.MessageQueue = append(state.MessageQueue, msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EmptyMessageQueue() {
 | 
			
		||||
  state.MessageQueue = make([]*discordgo.Message, 0)
 | 
			
		||||
  state.MessageQueue = make([]discord.Message, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetLastChannel(guild string, channel string) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue