github.com/diamondburned/arikawa@v1.3.14/api/guild.go (about)

     1  package api
     2  
     3  import (
     4  	"io"
     5  	"net/url"
     6  
     7  	"github.com/diamondburned/arikawa/discord" // for clarity
     8  	"github.com/diamondburned/arikawa/utils/httputil"
     9  	"github.com/diamondburned/arikawa/utils/json/option"
    10  )
    11  
    12  // maxGuildFetchLimit is the limit of max guilds per request, as imposed by
    13  // Discord.
    14  const maxGuildFetchLimit = 100
    15  
    16  var EndpointGuilds = Endpoint + "guilds/"
    17  
    18  // https://discordapp.com/developers/docs/resources/guild#create-guild-json-params
    19  type CreateGuildData struct {
    20  	// Name is the 	name of the guild (2-100 characters)
    21  	Name string `json:"name"`
    22  	// VoiceRegion is the voice region id.
    23  	VoiceRegion string `json:"region,omitempty"`
    24  	// Icon is the base64 128x128 image for the guild icon.
    25  	Icon *Image `json:"image,omitempty"`
    26  
    27  	// Verification is the 	verification level.
    28  	Verification *discord.Verification `json:"verification_level,omitempty"`
    29  	// Notification is the 	default message notification level.
    30  	Notification *discord.Notification `json:"default_message_notifications,omitempty"`
    31  	// ExplicitFilter is the explicit content filter level.
    32  	ExplicitFilter *discord.ExplicitFilter `json:"explicit_content_filter,omitempty"`
    33  
    34  	// Roles are the new guild roles.
    35  	//
    36  	// When using the roles parameter, the first member of the array is used to
    37  	// change properties of the guild's @everyone role. If you are trying to
    38  	// bootstrap a guild with additional roles, keep this in mind.
    39  	//
    40  	// When using the roles parameter, the required id field within each role
    41  	// object is an integer placeholder, and will be replaced by the API upon
    42  	// consumption. Its purpose is to allow you to overwrite a role's
    43  	// permissions in a channel when also passing in channels with the channels
    44  	// array.
    45  	Roles []discord.Role `json:"roles,omitempty"`
    46  	// Channels are the new guild's channels.
    47  	// Assigning a channel to a channel category is not supported by this
    48  	// endpoint, i.e. a channel can't have the parent_id field.
    49  	//
    50  	// When using the channels parameter, the position field is ignored,
    51  	// and none of the default channels are created.
    52  	//
    53  	// When using the channels parameter, the id field within each channel
    54  	// object may be set to an integer placeholder, and will be replaced by the
    55  	// API upon consumption. Its purpose is to allow you to create
    56  	// GUILD_CATEGORY channels by setting the parent_id field on any children
    57  	// to the category's id field. Category channels must be listed before any
    58  	// children.
    59  	Channels []discord.Channel `json:"channels,omitempty"`
    60  
    61  	// AFKChannelID is the id for the afk channel.
    62  	AFKChannelID discord.ChannelID `json:"afk_channel_id,omitempty"`
    63  	// AFKTimeout is the afk timeout in seconds.
    64  	AFKTimeout option.Seconds `json:"afk_timeout,omitempty"`
    65  
    66  	// SystemChannelID is the id of the channel where guild notices such as
    67  	// welcome messages and boost events are posted.
    68  	SystemChannelID discord.ChannelID `json:"system_channel_id,omitempty"`
    69  }
    70  
    71  // CreateGuild creates a new guild. Returns a guild object on success.
    72  // Fires a Guild Create Gateway event.
    73  //
    74  // This endpoint can be used only by bots in less than 10 guilds.
    75  func (c *Client) CreateGuild(data CreateGuildData) (*discord.Guild, error) {
    76  	var g *discord.Guild
    77  	return g, c.RequestJSON(&g, "POST", Endpoint+"guilds", httputil.WithJSONBody(data))
    78  }
    79  
    80  // Guild returns the guild object for the given id.
    81  // ApproximateMembers and ApproximatePresences will not be set.
    82  func (c *Client) Guild(id discord.GuildID) (*discord.Guild, error) {
    83  	var g *discord.Guild
    84  	return g, c.RequestJSON(&g, "GET", EndpointGuilds+id.String())
    85  }
    86  
    87  // GuildPreview returns the guild preview object for the given id, even if the
    88  // user is not in the guild.
    89  //
    90  // This endpoint is only for public guilds.
    91  func (c *Client) GuildPreview(id discord.GuildID) (*discord.GuildPreview, error) {
    92  	var g *discord.GuildPreview
    93  	return g, c.RequestJSON(&g, "GET", EndpointGuilds+id.String()+"/preview")
    94  }
    95  
    96  // GuildWithCount returns the guild object for the given id.
    97  // This will also set the ApproximateMembers and ApproximatePresences fields
    98  // of the guild struct.
    99  func (c *Client) GuildWithCount(id discord.GuildID) (*discord.Guild, error) {
   100  	var g *discord.Guild
   101  	return g, c.RequestJSON(
   102  		&g, "GET",
   103  		EndpointGuilds+id.String(),
   104  		httputil.WithSchema(c, url.Values{
   105  			"with_counts": {"true"},
   106  		}),
   107  	)
   108  }
   109  
   110  // Guilds returns a list of partial guild objects the current user is a member
   111  // of. This method automatically paginates until it reaches the passed limit,
   112  // or, if the limit is set to 0, has fetched all guilds the user has joined.
   113  //
   114  // As the underlying endpoint has a maximum of 100 guilds per request, at
   115  // maximum a total of limit/100 rounded up requests will be made, although they
   116  // may be less, if no more guilds are available.
   117  //
   118  // When fetching the guilds, those with the smallest ID will be fetched first.
   119  //
   120  // Also note that 100 is the maximum number of guilds a non-bot user can join.
   121  // Therefore, pagination is not needed for integrations that need to get a list
   122  // of the users' guilds.
   123  //
   124  // Requires the guilds OAuth2 scope.
   125  func (c *Client) Guilds(limit uint) ([]discord.Guild, error) {
   126  	return c.GuildsAfter(0, limit)
   127  }
   128  
   129  // GuildsBefore returns a list of partial guild objects the current user is a
   130  // member of. This method automatically paginates until it reaches the
   131  // passed limit, or, if the limit is set to 0, has fetched all guilds with an
   132  // id smaller than before.
   133  //
   134  // As the underlying endpoint has a maximum of 100 guilds per request, at
   135  // maximum a total of limit/100 rounded up requests will be made, although they
   136  // may be less, if no more guilds are available.
   137  //
   138  // Requires the guilds OAuth2 scope.
   139  func (c *Client) GuildsBefore(before discord.GuildID, limit uint) ([]discord.Guild, error) {
   140  	guilds := make([]discord.Guild, 0, limit)
   141  
   142  	fetch := uint(maxGuildFetchLimit)
   143  
   144  	unlimited := limit == 0
   145  
   146  	for limit > 0 || unlimited {
   147  		if limit > 0 {
   148  			// Only fetch as much as we need. Since limit gradually decreases,
   149  			// we only need to fetch min(fetch, limit).
   150  			if fetch > limit {
   151  				fetch = limit
   152  			}
   153  			limit -= fetch
   154  		}
   155  
   156  		g, err := c.guildsRange(before, 0, fetch)
   157  		if err != nil {
   158  			return guilds, err
   159  		}
   160  		guilds = append(g, guilds...)
   161  
   162  		if len(g) < maxGuildFetchLimit {
   163  			break
   164  		}
   165  
   166  		before = g[0].ID
   167  	}
   168  
   169  	if len(guilds) == 0 {
   170  		return nil, nil
   171  	}
   172  
   173  	return guilds, nil
   174  }
   175  
   176  // GuildsAfter returns a list of partial guild objects the current user is a
   177  // member of. This method automatically paginates until it reaches the
   178  // passed limit, or, if the limit is set to 0, has fetched all guilds with an
   179  // id higher than after.
   180  //
   181  // As the underlying endpoint has a maximum of 100 guilds per request, at
   182  // maximum a total of limit/100 rounded up requests will be made, although they
   183  // may be less, if no more guilds are available.
   184  //
   185  // Requires the guilds OAuth2 scope.
   186  func (c *Client) GuildsAfter(after discord.GuildID, limit uint) ([]discord.Guild, error) {
   187  	guilds := make([]discord.Guild, 0, limit)
   188  
   189  	fetch := uint(maxGuildFetchLimit)
   190  
   191  	unlimited := limit == 0
   192  
   193  	for limit > 0 || unlimited {
   194  		// Only fetch as much as we need. Since limit gradually decreases,
   195  		// we only need to fetch min(fetch, limit).
   196  		if limit > 0 {
   197  			if fetch > limit {
   198  				fetch = limit
   199  			}
   200  			limit -= fetch
   201  		}
   202  
   203  		g, err := c.guildsRange(0, after, fetch)
   204  		if err != nil {
   205  			return guilds, err
   206  		}
   207  		guilds = append(guilds, g...)
   208  
   209  		if len(g) < maxGuildFetchLimit {
   210  			break
   211  		}
   212  
   213  		after = g[len(g)-1].ID
   214  	}
   215  
   216  	if len(guilds) == 0 {
   217  		return nil, nil
   218  	}
   219  
   220  	return guilds, nil
   221  }
   222  
   223  func (c *Client) guildsRange(
   224  	before, after discord.GuildID, limit uint) ([]discord.Guild, error) {
   225  
   226  	var param struct {
   227  		Before discord.GuildID `schema:"before,omitempty"`
   228  		After  discord.GuildID `schema:"after,omitempty"`
   229  
   230  		Limit uint `schema:"limit"`
   231  	}
   232  
   233  	param.Before = before
   234  	param.After = after
   235  	param.Limit = limit
   236  
   237  	var gs []discord.Guild
   238  	return gs, c.RequestJSON(
   239  		&gs, "GET",
   240  		EndpointMe+"/guilds",
   241  		httputil.WithSchema(c, param),
   242  	)
   243  }
   244  
   245  // LeaveGuild leaves a guild.
   246  func (c *Client) LeaveGuild(id discord.GuildID) error {
   247  	return c.FastRequest("DELETE", EndpointMe+"/guilds/"+id.String())
   248  }
   249  
   250  // https://discordapp.com/developers/docs/resources/guild#modify-guild-json-params
   251  type ModifyGuildData struct {
   252  	// Name is the guild's name.
   253  	Name string `json:"name,omitempty"`
   254  	// Region is the guild's voice region id.
   255  	Region option.NullableString `json:"region,omitempty"`
   256  
   257  	// Verification is the verification level.
   258  	//
   259  	// This field is nullable.
   260  	Verification *discord.Verification `json:"verification_level,omitempty"`
   261  	// Notification is the default message notification level.
   262  	//
   263  	// This field is nullable.
   264  	Notification *discord.Notification `json:"default_message_notifications,omitempty"`
   265  	// ExplicitFilter is the explicit content filter level.
   266  	//
   267  	// This field is nullable.
   268  	ExplicitFilter *discord.ExplicitFilter `json:"explicit_content_filter,omitempty"`
   269  
   270  	// AFKChannelID is the id for the afk channel.
   271  	//
   272  	// This field is nullable.
   273  	AFKChannelID discord.ChannelID `json:"afk_channel_id,string,omitempty"`
   274  	// AFKTimeout is the afk timeout in seconds.
   275  	AFKTimeout option.Seconds `json:"afk_timeout,omitempty"`
   276  	// Icon is the base64 1024x1024 png/jpeg/gif image for the guild icon
   277  	// (can be animated gif when the server has the ANIMATED_ICON feature).
   278  	Icon *Image `json:"icon,omitempty"`
   279  	// Splash is the base64 16:9 png/jpeg image for the guild splash
   280  	// (when the server has the INVITE_SPLASH feature).
   281  	Splash *Image `json:"splash,omitempty"`
   282  	// Banner is the base64 16:9 png/jpeg image for the guild banner (when the
   283  	// server has BANNER feature).
   284  	Banner *Image `json:"banner,omitempty"`
   285  
   286  	// OwnerID is the user id to transfer guild ownership to (must be owner).
   287  	OwnerID discord.UserID `json:"owner_id,omitempty"`
   288  
   289  	// SystemChannelID is the id of the channel where guild notices such as
   290  	// welcome messages and boost events are posted.
   291  	//
   292  	// This field is nullable.
   293  	SystemChannelID discord.ChannelID `json:"system_channel_id,omitempty"`
   294  	// RulesChannelID is the id of the channel where "PUBLIC" guilds display
   295  	// rules and/or guidelines.
   296  	//
   297  	// This field is nullable.
   298  	RulesChannelID discord.ChannelID `json:"rules_channel_id,omitempty"`
   299  	// PublicUpdatesChannelID is the id of the channel where admins and
   300  	// moderators of "PUBLIC" guilds receive notices from Discord.
   301  	//
   302  	// This field is nullable.
   303  	PublicUpdatesChannelID discord.ChannelID `json:"public_updates_channel_id,omitempty"`
   304  
   305  	// PreferredLocale is the preferred locale of a "PUBLIC" guild used in
   306  	// server discovery and notices from Discord.
   307  	//
   308  	// This defaults to "en-US".
   309  	PreferredLocale option.NullableString `json:"preferred_locale,omitempty"`
   310  }
   311  
   312  // ModifyGuild modifies a guild's settings. Requires the MANAGE_GUILD permission.
   313  // Fires a Guild Update Gateway event.
   314  func (c *Client) ModifyGuild(id discord.GuildID, data ModifyGuildData) (*discord.Guild, error) {
   315  	var g *discord.Guild
   316  	return g, c.RequestJSON(
   317  		&g, "PATCH",
   318  		EndpointGuilds+id.String(),
   319  		httputil.WithJSONBody(data),
   320  	)
   321  
   322  }
   323  
   324  // DeleteGuild deletes a guild permanently. The User must be owner.
   325  //
   326  // Fires a Guild Delete Gateway event.
   327  func (c *Client) DeleteGuild(id discord.GuildID) error {
   328  	return c.FastRequest("DELETE", EndpointGuilds+id.String())
   329  }
   330  
   331  // GuildVoiceRegions is the same as /voice, but returns VIP ones as well if
   332  // available.
   333  func (c *Client) VoiceRegionsGuild(guildID discord.GuildID) ([]discord.VoiceRegion, error) {
   334  	var vrs []discord.VoiceRegion
   335  	return vrs, c.RequestJSON(&vrs, "GET", EndpointGuilds+guildID.String()+"/regions")
   336  }
   337  
   338  // https://discord.com/developers/docs/resources/audit-log#get-guild-audit-log-query-string-parameters
   339  type AuditLogData struct {
   340  	// UserID filters the log for actions made by a user.
   341  	UserID discord.UserID `schema:"user_id,omitempty"`
   342  	// ActionType is the type of audit log event.
   343  	ActionType discord.AuditLogEvent `schema:"action_type,omitempty"`
   344  	// Before filters the log before a certain entry ID.
   345  	Before discord.AuditLogEntryID `schema:"before,omitempty"`
   346  	// Limit limits how many entries are returned (default 50, minimum 1,
   347  	// maximum 100).
   348  	Limit uint `schema:"limit"`
   349  }
   350  
   351  // AuditLog returns an audit log object for the guild.
   352  //
   353  // Requires the VIEW_AUDIT_LOG permission.
   354  func (c *Client) AuditLog(guildID discord.GuildID, data AuditLogData) (*discord.AuditLog, error) {
   355  	switch {
   356  	case data.Limit == 0:
   357  		data.Limit = 50
   358  	case data.Limit > 100:
   359  		data.Limit = 100
   360  	}
   361  
   362  	var audit *discord.AuditLog
   363  
   364  	return audit, c.RequestJSON(
   365  		&audit, "GET",
   366  		EndpointGuilds+guildID.String()+"/audit-logs",
   367  		httputil.WithSchema(c, data),
   368  	)
   369  }
   370  
   371  // Integrations returns a list of integration objects for the guild.
   372  //
   373  // Requires the MANAGE_GUILD permission.
   374  func (c *Client) Integrations(guildID discord.GuildID) ([]discord.Integration, error) {
   375  	var ints []discord.Integration
   376  	return ints, c.RequestJSON(&ints, "GET", EndpointGuilds+guildID.String()+"/integrations")
   377  }
   378  
   379  // AttachIntegration attaches an integration object from the current user to
   380  // the guild.
   381  //
   382  // Requires the MANAGE_GUILD permission.
   383  // Fires a Guild Integrations Update Gateway event.
   384  func (c *Client) AttachIntegration(
   385  	guildID discord.GuildID, integrationID discord.IntegrationID,
   386  	integrationType discord.Service) error {
   387  
   388  	var param struct {
   389  		Type discord.Service       `json:"type"`
   390  		ID   discord.IntegrationID `json:"id"`
   391  	}
   392  
   393  	param.Type = integrationType
   394  	param.ID = integrationID
   395  
   396  	return c.FastRequest(
   397  		"POST",
   398  		EndpointGuilds+guildID.String()+"/integrations",
   399  		httputil.WithJSONBody(param),
   400  	)
   401  }
   402  
   403  // https://discord.com/developers/docs/resources/guild#modify-guild-integration-json-params
   404  type ModifyIntegrationData struct {
   405  	// ExpireBehavior is the behavior when an integration subscription lapses
   406  	// (see the integration expire behaviors documentation).
   407  	ExpireBehavior *discord.ExpireBehavior `json:"expire_behavior,omitempty"`
   408  	// ExpireGracePeriod is the period (in days) where the integration will
   409  	// ignore lapsed subscriptions.
   410  	ExpireGracePeriod option.NullableInt `json:"expire_grace_period,omitempty"`
   411  	// EnableEmoticons specifies whether emoticons should be synced for this
   412  	// integration (twitch only currently).
   413  	EnableEmoticons option.NullableBool `json:"enable_emoticons,omitempty"`
   414  }
   415  
   416  // ModifyIntegration modifies the behavior and settings of an integration
   417  // object for the guild.
   418  //
   419  // Requires the MANAGE_GUILD permission.
   420  // Fires a Guild Integrations Update Gateway event.
   421  func (c *Client) ModifyIntegration(
   422  	guildID discord.GuildID, integrationID discord.IntegrationID, data ModifyIntegrationData) error {
   423  	return c.FastRequest(
   424  		"PATCH",
   425  		EndpointGuilds+guildID.String()+"/integrations/"+integrationID.String(),
   426  		httputil.WithJSONBody(data),
   427  	)
   428  }
   429  
   430  // Sync an integration. Requires the MANAGE_GUILD permission.
   431  func (c *Client) SyncIntegration(guildID discord.GuildID, integrationID discord.IntegrationID) error {
   432  	return c.FastRequest(
   433  		"POST",
   434  		EndpointGuilds+guildID.String()+"/integrations/"+integrationID.String()+"/sync",
   435  	)
   436  }
   437  
   438  // GuildWidget returns the guild widget object.
   439  //
   440  // Requires the MANAGE_GUILD permission.
   441  func (c *Client) GuildWidget(guildID discord.GuildID) (*discord.GuildWidget, error) {
   442  	var ge *discord.GuildWidget
   443  	return ge, c.RequestJSON(&ge, "GET", EndpointGuilds+guildID.String()+"/widget")
   444  }
   445  
   446  // https://discord.com/developers/docs/resources/guild#guild-embed-object-guild-embed-structure
   447  type ModifyGuildWidgetData struct {
   448  	// Enabled specifies whether the widget is enabled.
   449  	Enabled option.Bool `json:"enabled,omitempty"`
   450  	// ChannelID is the widget channel id.
   451  	ChannelID discord.ChannelID `json:"channel_id,omitempty"`
   452  }
   453  
   454  // ModifyGuildWidget modifies a guild widget object for the guild.
   455  //
   456  // Requires the MANAGE_GUILD permission.
   457  func (c *Client) ModifyGuildWidget(
   458  	guildID discord.GuildID, data ModifyGuildWidgetData) (*discord.GuildWidget, error) {
   459  
   460  	var w *discord.GuildWidget
   461  	return w, c.RequestJSON(
   462  		&w, "PATCH",
   463  		EndpointGuilds+guildID.String()+"/widget",
   464  		httputil.WithJSONBody(data),
   465  	)
   466  }
   467  
   468  // GuildVanityURL returns *Invite for guilds that have that feature enabled,
   469  // but only Code and Uses are filled. Code will be "" if a vanity url for the
   470  // guild is not set.
   471  //
   472  // Requires MANAGE_GUILD.
   473  func (c *Client) GuildVanityURL(guildID discord.GuildID) (*discord.Invite, error) {
   474  	var inv *discord.Invite
   475  	return inv, c.RequestJSON(&inv, "GET", EndpointGuilds+guildID.String()+"/vanity-url")
   476  }
   477  
   478  // https://discord.com/developers/docs/resources/guild#get-guild-widget-image-widget-style-options
   479  type GuildImageStyle string
   480  
   481  const (
   482  	// GuildShield is a shield style widget with Discord icon and guild members
   483  	// online count.
   484  	//
   485  	// Example: https://discordapp.com/api/guilds/81384788765712384/widget.png?style=shield
   486  	GuildShield GuildImageStyle = "shield"
   487  	// GuildBanner1 is a large image with guild icon, name and online count.
   488  	// "POWERED BY DISCORD" as the footer of the widget.
   489  	//
   490  	// Example: https://discordapp.com/api/guilds/81384788765712384/widget.png?style=banner1
   491  	GuildBanner1 GuildImageStyle = "banner1"
   492  	// GuildBanner2 is a smaller widget style with guild icon, name and online
   493  	// count. Split on the right with Discord logo.
   494  	//
   495  	// Example: https://discordapp.com/api/guilds/81384788765712384/widget.png?style=banner2
   496  	GuildBanner2 GuildImageStyle = "banner2"
   497  	// GuildBanner3 is a large image with guild icon, name and online count. In
   498  	// the footer, Discord logo on the left and "Chat Now" on the right.
   499  	//
   500  	// Example: https://discordapp.com/api/guilds/81384788765712384/widget.png?style=banner3
   501  	GuildBanner3 GuildImageStyle = "banner3"
   502  	// GuildBanner4 is a large Discord logo at the top of the widget.
   503  	// Guild icon, name and online count in the middle portion of the widget
   504  	// and a "JOIN MY SERVER" button at the bottom.
   505  	//
   506  	// Example: https://discordapp.com/api/guilds/81384788765712384/widget.png?style=banner4
   507  	GuildBanner4 GuildImageStyle = "banner4"
   508  )
   509  
   510  // GuildImageURL returns a link to the PNG image widget for the guild.
   511  //
   512  // Requires no permissions or authentication.
   513  func (c *Client) GuildImageURL(guildID discord.GuildID, img GuildImageStyle) string {
   514  	return EndpointGuilds + guildID.String() + "/widget.png?style=" + string(img)
   515  }
   516  
   517  // GuildImage returns a PNG image widget for the guild. Requires no permissions
   518  // or authentication.
   519  func (c *Client) GuildImage(guildID discord.GuildID, img GuildImageStyle) (io.ReadCloser, error) {
   520  	r, err := c.Request("GET", c.GuildImageURL(guildID, img))
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  
   525  	return r.GetBody(), nil
   526  }