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 }