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  }