github.com/starshine-sys/bcr@v0.21.0/parsers.go (about) 1 package bcr 2 3 import ( 4 "errors" 5 "regexp" 6 "strings" 7 8 "github.com/diamondburned/arikawa/v3/discord" 9 ) 10 11 var ( 12 channelMentionRegex = regexp.MustCompile("<#(\\d+)>") 13 userMentionRegex = regexp.MustCompile("<@!?(\\d+)>") 14 roleMentionRegex = regexp.MustCompile("<@&(\\d+)>") 15 16 idRegex = regexp.MustCompile("^\\d+$") 17 18 msgIDRegex = regexp.MustCompile(`(?P<channel_id>[0-9]{15,20})-(?P<message_id>[0-9]{15,20})$`) 19 msgLinkRegex = regexp.MustCompile(`https?://(?:(ptb|canary|www)\.)?discord(?:app)?\.com/channels/(?:[0-9]{15,20}|@me)/(?P<channel_id>[0-9]{15,20})/(?P<message_id>[0-9]{15,20})/?$`) 20 ) 21 22 // Errors related to parsing 23 var ( 24 ErrInvalidMention = errors.New("invalid mention") 25 ErrChannelNotFound = errors.New("channel not found") 26 ErrMemberNotFound = errors.New("member not found") 27 ErrUserNotFound = errors.New("user not found") 28 ErrRoleNotFound = errors.New("role not found") 29 ErrMessageNotFound = errors.New("message not found") 30 ) 31 32 // ParseChannel parses a channel mention/id/name 33 func (ctx *Context) ParseChannel(s string) (c *discord.Channel, err error) { 34 // check if it's an ID 35 if idRegex.MatchString(s) { 36 sf, err := discord.ParseSnowflake(s) 37 if err != nil { 38 return nil, err 39 } 40 return ctx.State.Channel(discord.ChannelID(sf)) 41 } 42 43 // check if it's a mention 44 if channelMentionRegex.MatchString(s) { 45 matches := channelMentionRegex.FindStringSubmatch(s) 46 if len(matches) < 2 { 47 return nil, ErrInvalidMention 48 } 49 sf, err := discord.ParseSnowflake(matches[1]) 50 if err != nil { 51 return nil, err 52 } 53 return ctx.State.Channel(discord.ChannelID(sf)) 54 } 55 56 // otherwise, fall back to names 57 channels, err := ctx.State.Channels(ctx.Message.GuildID) 58 if err != nil { 59 return nil, err 60 } 61 62 for _, ch := range channels { 63 if strings.ToLower(s) == strings.ToLower(ch.Name) { 64 return &ch, nil 65 } 66 } 67 return nil, ErrChannelNotFound 68 } 69 70 // ParseMember parses a member mention/id/name 71 func (ctx *Context) ParseMember(s string) (c *discord.Member, err error) { 72 // check if it's an ID 73 if idRegex.MatchString(s) { 74 sf, err := discord.ParseSnowflake(s) 75 if err != nil { 76 return nil, err 77 } 78 return ctx.State.Member(ctx.Message.GuildID, discord.UserID(sf)) 79 } 80 81 // check if it's a mention 82 if userMentionRegex.MatchString(s) { 83 matches := userMentionRegex.FindStringSubmatch(s) 84 if len(matches) < 2 { 85 return nil, ErrInvalidMention 86 } 87 sf, err := discord.ParseSnowflake(matches[1]) 88 if err != nil { 89 return nil, err 90 } 91 return ctx.State.Member(ctx.Message.GuildID, discord.UserID(sf)) 92 } 93 94 // otherwise, fall back to names 95 members, err := ctx.State.Members(ctx.Message.GuildID) 96 if err != nil { 97 return nil, err 98 } 99 100 for _, m := range members { 101 // check full name 102 if strings.ToLower(m.User.Username)+"#"+m.User.Discriminator == strings.ToLower(s) { 103 return &m, nil 104 } 105 106 // check just username 107 if strings.ToLower(m.User.Username) == strings.ToLower(s) { 108 return &m, nil 109 } 110 111 // check nickname 112 if strings.ToLower(m.Nick) == strings.ToLower(s) { 113 return &m, nil 114 } 115 } 116 return nil, ErrMemberNotFound 117 } 118 119 // ParseRole parses a role mention/id/name 120 func (ctx *Context) ParseRole(s string) (c *discord.Role, err error) { 121 // check if it's an ID 122 if idRegex.MatchString(s) { 123 sf, err := discord.ParseSnowflake(s) 124 if err != nil { 125 return nil, err 126 } 127 return ctx.State.Role(ctx.Message.GuildID, discord.RoleID(sf)) 128 } 129 130 // check if it's a mention 131 if roleMentionRegex.MatchString(s) { 132 matches := roleMentionRegex.FindStringSubmatch(s) 133 if len(matches) < 2 { 134 return nil, ErrInvalidMention 135 } 136 sf, err := discord.ParseSnowflake(matches[1]) 137 if err != nil { 138 return nil, err 139 } 140 return ctx.State.Role(ctx.Message.GuildID, discord.RoleID(sf)) 141 } 142 143 // otherwise, fall back to names 144 roles, err := ctx.State.Roles(ctx.Message.GuildID) 145 if err != nil { 146 return nil, err 147 } 148 149 for _, r := range roles { 150 if strings.ToLower(s) == strings.ToLower(r.Name) { 151 return &r, nil 152 } 153 } 154 return nil, ErrChannelNotFound 155 } 156 157 // ParseUser finds a user by mention or ID 158 func (ctx *Context) ParseUser(s string) (u *discord.User, err error) { 159 if idRegex.MatchString(s) { 160 sf, err := discord.ParseSnowflake(s) 161 if err != nil { 162 return nil, err 163 } 164 return ctx.State.User(discord.UserID(sf)) 165 } 166 167 if userMentionRegex.MatchString(s) { 168 matches := userMentionRegex.FindStringSubmatch(s) 169 if len(matches) < 2 { 170 return nil, ErrInvalidMention 171 } 172 sf, err := discord.ParseSnowflake(matches[1]) 173 if err != nil { 174 return nil, err 175 } 176 return ctx.State.User(discord.UserID(sf)) 177 } 178 179 return nil, ErrUserNotFound 180 } 181 182 // ParseMessage parses a message link or ID. 183 // Either in channelID-messageID format (obtained by shift right-clicking on the "copy ID" button in the desktop client), or the message link obtained with the "copy message link" button. 184 // Will error if the bot does not have access to the channel the message is in. 185 func (ctx *Context) ParseMessage(s string) (m *discord.Message, err error) { 186 var groups []string 187 188 if msgIDRegex.MatchString(s) { 189 groups = msgIDRegex.FindStringSubmatch(s) 190 } else if msgLinkRegex.MatchString(s) { 191 groups = msgLinkRegex.FindStringSubmatch(s) 192 groups = groups[1:] 193 } 194 195 if len(groups) == 0 { 196 return nil, ErrMessageNotFound 197 } 198 199 channel, _ := discord.ParseSnowflake(groups[1]) 200 msgID, _ := discord.ParseSnowflake(groups[2]) 201 202 m, err = ctx.State.Message(discord.ChannelID(channel), discord.MessageID(msgID)) 203 if err != nil { 204 return m, ErrMessageNotFound 205 } 206 return 207 }