github.com/starshine-sys/bcr@v0.21.0/paged_embed.go (about)

     1  package bcr
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  	"time"
     8  
     9  	"github.com/diamondburned/arikawa/v3/discord"
    10  )
    11  
    12  // ErrNoEmbeds is returned if PagedEmbed() is called without any embeds
    13  var ErrNoEmbeds = errors.New("PagedEmbed: no embeds")
    14  
    15  // PagedEmbed sends a slice of embeds, and attaches reaction handlers to flip through them.
    16  // if extendedReactions is true, also add delete, first page, and last page reactions.
    17  func (ctx *Context) PagedEmbed(embeds []discord.Embed, extendedReactions bool) (msg *discord.Message, err error) {
    18  	msg, _, err = ctx.PagedEmbedTimeout(embeds, extendedReactions, 15*time.Minute)
    19  	return
    20  }
    21  
    22  // PagedEmbedTimeout creates a paged embed (see PagedEmbed) that times out after the given time.
    23  // It also returns a timer that can be used to cancel the attached reaction-clearing timer.
    24  func (ctx *Context) PagedEmbedTimeout(embeds []discord.Embed, extendedReactions bool, timeout time.Duration) (msg *discord.Message, timer *time.Timer, err error) {
    25  	// if there's no embeds, return
    26  	if len(embeds) == 0 {
    27  		return nil, nil, ErrNoEmbeds
    28  	}
    29  
    30  	// set additional parameters, used for pagination
    31  	ctx.AdditionalParams["page"] = 0
    32  	ctx.AdditionalParams["embeds"] = embeds
    33  
    34  	// send the first embed
    35  	msg, err = ctx.Send("", embeds[0])
    36  	if err != nil {
    37  		return
    38  	}
    39  
    40  	// this doesn't seem to work rn for some reason
    41  	// timer = time.AfterFunc(timeout, func() {
    42  	// 	ctx.State.DeleteAllReactions(msg.ChannelID, msg.ID)
    43  	// })
    44  
    45  	// add :x: handler
    46  	ctx.AddReactionHandlerWithTimeout(msg.ID, ctx.Author.ID, "❌", true, false, timeout, func(*Context) {
    47  		// timer.Stop()
    48  		err = ctx.State.DeleteMessage(ctx.Channel.ID, msg.ID, "")
    49  		if err != nil {
    50  			ctx.Router.Logger.Error("deleting message: %v", err)
    51  		}
    52  	})
    53  
    54  	// if there's only one embed, that's it! no pager emoji needed
    55  	if len(embeds) == 1 {
    56  		return
    57  	}
    58  
    59  	// react with all required emoji--afawk there's no more concise way to do this
    60  	if extendedReactions {
    61  		if err = ctx.State.React(ctx.Channel.ID, msg.ID, "❌"); err != nil {
    62  			return
    63  		}
    64  		if err = ctx.State.React(ctx.Channel.ID, msg.ID, "⏪"); err != nil {
    65  			return
    66  		}
    67  	}
    68  	if err = ctx.State.React(ctx.Channel.ID, msg.ID, "⬅️"); err != nil {
    69  		return
    70  	}
    71  	if err = ctx.State.React(ctx.Channel.ID, msg.ID, "➡️"); err != nil {
    72  		return
    73  	}
    74  	if extendedReactions {
    75  		if err = ctx.State.React(ctx.Channel.ID, msg.ID, "⏩"); err != nil {
    76  			return
    77  		}
    78  	}
    79  
    80  	// add handlers for the reactions
    81  	ctx.AddReactionHandlerWithTimeoutRemove(msg.ID, ctx.Author.ID, "⬅️", false, true, timeout, func(ctx *Context) {
    82  		page := ctx.AdditionalParams["page"].(int)
    83  		embeds := ctx.AdditionalParams["embeds"].([]discord.Embed)
    84  
    85  		if page == 0 {
    86  			ctx.State.EditEmbeds(ctx.Channel.ID, msg.ID, embeds[len(embeds)-1])
    87  			ctx.AdditionalParams["page"] = len(embeds) - 1
    88  			return
    89  		}
    90  		ctx.State.EditEmbeds(ctx.Channel.ID, msg.ID, embeds[page-1])
    91  		ctx.AdditionalParams["page"] = page - 1
    92  	})
    93  
    94  	ctx.AddReactionHandlerWithTimeoutRemove(msg.ID, ctx.Author.ID, "➡️", false, true, timeout, func(ctx *Context) {
    95  		page := ctx.AdditionalParams["page"].(int)
    96  		embeds := ctx.AdditionalParams["embeds"].([]discord.Embed)
    97  
    98  		if page >= len(embeds)-1 {
    99  			ctx.State.EditEmbeds(ctx.Channel.ID, msg.ID, embeds[0])
   100  			ctx.AdditionalParams["page"] = 0
   101  			return
   102  		}
   103  		ctx.State.EditEmbeds(ctx.Channel.ID, msg.ID, embeds[page+1])
   104  		ctx.AdditionalParams["page"] = page + 1
   105  	})
   106  
   107  	if extendedReactions {
   108  		ctx.AddReactionHandlerWithTimeoutRemove(msg.ID, ctx.Author.ID, "⏪", false, true, timeout, func(ctx *Context) {
   109  			embeds := ctx.AdditionalParams["embeds"].([]discord.Embed)
   110  
   111  			ctx.State.EditEmbeds(ctx.Channel.ID, msg.ID, embeds[0])
   112  			ctx.AdditionalParams["page"] = 0
   113  		})
   114  
   115  		ctx.AddReactionHandlerWithTimeoutRemove(msg.ID, ctx.Author.ID, "⏩", false, true, timeout, func(ctx *Context) {
   116  			embeds := ctx.AdditionalParams["embeds"].([]discord.Embed)
   117  
   118  			ctx.State.EditEmbeds(ctx.Channel.ID, msg.ID, embeds[len(embeds)-1])
   119  			ctx.AdditionalParams["page"] = len(embeds) - 1
   120  		})
   121  	}
   122  	return
   123  }
   124  
   125  // FieldPaginator paginates embed fields, for use in ctx.PagedEmbed
   126  func FieldPaginator(title, description string, colour discord.Color, fields []discord.EmbedField, perPage int) []discord.Embed {
   127  	var (
   128  		embeds []discord.Embed
   129  		count  int
   130  
   131  		pages = 1
   132  		buf   = discord.Embed{
   133  			Title:       title,
   134  			Description: description,
   135  			Color:       colour,
   136  			Footer: &discord.EmbedFooter{
   137  				Text: fmt.Sprintf("Page 1/%v", math.Ceil(float64(len(fields))/float64(perPage))),
   138  			},
   139  		}
   140  	)
   141  
   142  	for _, field := range fields {
   143  		if count >= perPage {
   144  			embeds = append(embeds, buf)
   145  			buf = discord.Embed{
   146  				Title:       title,
   147  				Description: description,
   148  				Color:       colour,
   149  				Footer: &discord.EmbedFooter{
   150  					Text: fmt.Sprintf("Page %v/%v", pages+1, math.Ceil(float64(len(fields))/float64(perPage))),
   151  				},
   152  			}
   153  			count = 0
   154  			pages++
   155  		}
   156  		buf.Fields = append(buf.Fields, field)
   157  		count++
   158  	}
   159  
   160  	embeds = append(embeds, buf)
   161  
   162  	return embeds
   163  }
   164  
   165  // StringPaginator paginates strings, for use in ctx.PagedEmbed
   166  func StringPaginator(title string, colour discord.Color, slice []string, perPage int) []discord.Embed {
   167  	var (
   168  		embeds []discord.Embed
   169  		count  int
   170  
   171  		pages = 1
   172  		buf   = discord.Embed{
   173  			Title: title,
   174  			Color: colour,
   175  			Footer: &discord.EmbedFooter{
   176  				Text: fmt.Sprintf("Page 1/%v", math.Ceil(float64(len(slice))/float64(perPage))),
   177  			},
   178  		}
   179  	)
   180  
   181  	for _, s := range slice {
   182  		if count >= perPage {
   183  			embeds = append(embeds, buf)
   184  			buf = discord.Embed{
   185  				Title: title,
   186  				Color: colour,
   187  				Footer: &discord.EmbedFooter{
   188  					Text: fmt.Sprintf("Page %v/%v", pages+1, math.Ceil(float64(len(slice))/float64(perPage))),
   189  				},
   190  			}
   191  			count = 0
   192  			pages++
   193  		}
   194  		buf.Description += s
   195  		count++
   196  	}
   197  
   198  	embeds = append(embeds, buf)
   199  
   200  	return embeds
   201  }