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 }