github.com/diamondburned/arikawa/v2@v2.1.0/api/guild.go (about)

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