Exemple #1
0
func (p *topStreamersPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "topstreamers", message) {
			n := time.Now()
			if !n.After(p.lastUpdate.Add(1 * time.Minute)) {
				if p.lastMessage != "" {
					service.SendMessage(message.Channel(), fmt.Sprintf("%s *Last updated %s.*", p.lastMessage, humanize.Time(p.lastUpdate)))
				}
				return
			}

			service.Typing(message.Channel())

			p.lastUpdate = n

			m, err := p.topStreamers(5)
			if err != nil {
				service.SendMessage(message.Channel(), "There was an error while requesting the top streamers, please try again later.")
				return
			}

			service.SendMessage(message.Channel(), m)
			p.lastMessage = m
		}
	}
}
Exemple #2
0
func (t *triviaChannel) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if strings.ToLower(message.Message()) == t.Answer {
		t.Lock()
		defer t.Unlock()

		if !t.Active {
			return
		}

		ts := t.Scores[message.UserID()]
		if ts == nil {
			ts = &triviaScore{}
			t.Scores[message.UserID()] = ts
		}
		ts.Name = message.UserName()
		ts.Score++

		service.SendMessage(message.Channel(), fmt.Sprintf("%s got it! The answer was %s.", message.UserName(), t.Answer))
		service.SendMessage(message.Channel(), fmt.Sprintf("%s answered in %d seconds and their score is now %d.", message.UserName(), int(time.Now().Sub(t.Asked).Seconds()), ts.Score))

		t.Unanswered = 0

		if t.hintChan != nil {
			close(t.hintChan)
			t.hintChan = nil
		}
	}
}
Exemple #3
0
// SendReminder sends a reminder.
func (p *ReminderPlugin) SendReminder(service bruxism.Service, reminder *Reminder) {
	if reminder.IsPrivate {
		service.SendMessage(reminder.Target, fmt.Sprintf("%s you set a reminder: %s", humanize.Time(reminder.StartTime), reminder.Message))
	} else {
		service.SendMessage(reminder.Target, fmt.Sprintf("%s %s set a reminder: %s", humanize.Time(reminder.StartTime), reminder.Requester, reminder.Message))
	}
}
Exemple #4
0
func makeScriptFromMessages(service bruxism.Service, message bruxism.Message, messages []bruxism.Message) *comicgen.Script {
	speakers := make(map[string]int)
	avatars := make(map[int]string)

	script := []*comicgen.Message{}

	for _, message := range messages {
		speaker, ok := speakers[message.UserName()]
		if !ok {
			speaker = len(speakers)
			speakers[message.UserName()] = speaker
			avatars[speaker] = message.UserAvatar()
		}

		script = append(script, &comicgen.Message{
			Speaker: speaker,
			Text:    message.Message(),
			Author:  message.UserName(),
		})
	}
	return &comicgen.Script{
		Messages: script,
		Author:   fmt.Sprintf(service.UserName()),
		Avatars:  avatars,
		Type:     comicgen.ComicTypeChat,
	}
}
Exemple #5
0
func (p *carbonitexPlugin) carbonitexPluginLoadFunc(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Carbonitex Plugin only supports Discord.")
	}

	go p.Run(bot, service)
	return nil
}
Exemple #6
0
func (p *ReminderPlugin) randomReminder(service bruxism.Service) string {
	ticks := ""
	if service.Name() == bruxism.DiscordServiceName {
		ticks = "`"
	}

	return fmt.Sprintf("%s%sreminder %s %s%s", ticks, service.CommandPrefix(), p.random(randomTimes), p.random(randomMessages), ticks)
}
Exemple #7
0
func (p *chartPlugin) randomChart(service bruxism.Service) string {
	ticks := ""
	if service.Name() == bruxism.DiscordServiceName {
		ticks = "`"
	}

	return fmt.Sprintf("%s%schart %s %s, %s%s", ticks, service.CommandPrefix(), p.random(randomDirection), p.random(randomY), p.random(randomX), ticks)
}
Exemple #8
0
// Help returns a list of help strings that are printed when the user requests them.
func (p *playingPlugin) helpFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if detailed {
		return nil
	}

	if !service.IsBotOwner(message) {
		return nil
	}

	return bruxism.CommandHelp(service, "playing", "<game>, <url>", fmt.Sprintf("Set which game %s is playing.", service.UserName()))
}
Exemple #9
0
// Message handler.
func (p *YouTubeJoinPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if (service.IsBotOwner(message) || service.IsChannelOwner(message)) && bruxism.MatchesCommand(service, "leave", message) {
		video, ok := p.youtube.VideoIDForChatID(message.Channel())
		if ok {
			channel, ok := p.youtube.ChannelIDForVideoID(video)
			if ok {
				p.Unmonitor(channel)
			}
		}
	}
}
Exemple #10
0
// Run will poll YouTube for channels going live and send messages.
func (p *livePlugin) Run(bot *bruxism.Bot, service bruxism.Service) {
	p.RLock()
	lvc := p.liveVideoChan
	p.RUnlock()
	for {
		v := <-lvc
		p.RLock()
		for channel := range p.youTubeChannelToChannels[v.Snippet.ChannelId] {
			service.SendMessage(channel, fmt.Sprintf("%s has just gone live! http://gaming.youtube.com/watch?v=%s", v.Snippet.ChannelTitle, v.Id))
		}
		p.RUnlock()
	}
}
Exemple #11
0
// MTGCommand is a command for getting information about MTG cards..
func MTGCommand(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, command string, parts []string) {
	cardNames := fuzzy.RankFindFold(command, MTGCardNames)
	if len(cardNames) == 0 {
		service.SendMessage(message.Channel(), "Could not find a card with that name, sorry.")
		return
	}

	sort.Sort(cardNames)

	card := MTGCardMap[cardNames[0].Target]

	rest := ""
	if card.Text != "" {
		rest += "\n"
	}
	if card.Power != nil {
		rest += MTGRestReplacer.Replace(fmt.Sprintf("%s/%s", *card.Power, *card.Toughness))
	}
	if card.Loyalty != nil {
		rest += MTGRestReplacer.Replace(fmt.Sprintf("%d", *card.Loyalty))
	}
	if card.ID != nil {
		if rest != "" && rest != "\n" {
			rest += "\n"
		}
		rest += fmt.Sprintf("(http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=%d&type=card)", *card.ID)
	}

	if service.Name() == bruxism.DiscordServiceName {
		service.SendMessage(message.Channel(), fmt.Sprintf("**%s** %s\n*%s*\n%s%s", card.Name, card.ManaCost, card.Type, MTGTextReplacer.Replace(card.Text), rest))
	} else {
		service.SendMessage(message.Channel(), strings.Replace(fmt.Sprintf("%s. %s. %s. %s%s", card.Name, card.Type, card.ManaCost, card.Text, rest), "\n", " ", -1))
	}
}
Exemple #12
0
// InviteHelp will return the help text for the invite command.
func InviteHelp(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) (string, string) {
	switch service.Name() {
	case bruxism.DiscordServiceName:
		discord := service.(*bruxism.Discord)

		if discord.ApplicationClientID != "" {
			return "", fmt.Sprintf("Returns a URL to add %s to your server.", service.UserName())
		}
		return "<discordinvite>", "Joins the provided Discord server."
	case bruxism.YouTubeServiceName:
		return "<videoid>", "Joins the provided YouTube live stream."
	}
	return "<channel>", "Joins the provided channel."
}
Exemple #13
0
// Load will load plugin state from a byte array.
func (p *playedPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Played Plugin only supports Discord.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	go p.Run(bot, service)
	return nil
}
Exemple #14
0
// Load will load plugin state from a byte array.
func (p *MusicPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) (err error) {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Music Plugin only supports Discord.")
	}

	if data != nil {
		if err = json.Unmarshal(data, p); err != nil {
			log.Println("musicplugin: loading data err:", err)
		}
	}

	go p.init()

	return nil
}
Exemple #15
0
func (t *triviaChannel) Stop(bot *bruxism.Bot, service bruxism.Service) {
	t.Lock()
	defer t.Unlock()

	if !t.Active {
		return
	}

	t.Active = false
	if t.hintChan != nil {
		close(t.hintChan)
		t.hintChan = nil
	}

	service.SendMessage(t.Channel, "Trivia stopped.")

	return
}
Exemple #16
0
func (t *triviaChannel) Start(bot *bruxism.Bot, service bruxism.Service, theme string) {
	t.Lock()
	defer t.Unlock()

	if t.Active {
		return
	}

	service.SendMessage(t.Channel, "Trivia started.")

	t.Active = true
	t.Theme = theme
	t.Unanswered = 0

	go t.question(bot, service)

	return
}
Exemple #17
0
// Load will load plugin state from a byte array.
func (p *playingPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Playing Plugin only supports Discord.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	if p.Game != "" {
		service.(*bruxism.Discord).Session.UpdateStreamingStatus(0, p.Game, p.URL)
	} else {
		service.(*bruxism.Discord).Session.UpdateStatus(0, p.Game)
	}

	return nil
}
Exemple #18
0
// Message handler.
func (p *playingPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "playing", message) {
			if !service.IsBotOwner(message) {
				return
			}
			query, _ := bruxism.ParseCommand(service, message)

			split := strings.Split(query, ",")

			p.Game = strings.Trim(split[0], " ")
			if len(split) > 1 {
				p.URL = strings.Trim(split[1], " ")
				service.(*bruxism.Discord).Session.UpdateStreamingStatus(0, p.Game, p.URL)
			} else {
				service.(*bruxism.Discord).Session.UpdateStatus(0, p.Game)
			}
		}
	}
}
Exemple #19
0
// Load will load plugin state from a byte array.
func (p *YouTubeJoinPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.YouTubeServiceName {
		panic("YouTubeJoin plugin only supports YouTube.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	p.youtube = service.(*bruxism.YouTube)

	for channel, _ := range p.Channels {
		p.monitor(channel, false)
	}

	go p.Run(bot, service)

	return nil
}
Exemple #20
0
func avatarMessageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "avatar", message) {
			query := strings.Join(strings.Split(message.RawMessage(), " ")[1:], " ")

			id := message.UserID()
			match := userIDRegex.FindStringSubmatch(query)
			if match != nil {
				id = match[1]
			}

			discord := service.(*bruxism.Discord)

			u, err := discord.Session.User(id)
			if err != nil {
				return
			}

			service.SendMessage(message.Channel(), discordgo.EndpointUserAvatar(u.ID, u.Avatar))
		}
	}
}
Exemple #21
0
// Help returns a list of help strings that are printed when the user requests them.
func (p *comicPlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	help := bruxism.CommandHelp(service, "comic", "[1-10]", "Creates a comic from recent messages, or a number of messages if provided.")

	ticks := ""
	if service.Name() == bruxism.DiscordServiceName {
		ticks = "`"
	}
	if detailed {
		help = append(help, []string{
			bruxism.CommandHelp(service, "customcomic", "[id|name:] <text> | [id|name:] <text>", fmt.Sprintf("Creates a custom comic. Available names: %s%s%s", ticks, strings.Join(comicgen.CharacterNames, ", "), ticks))[0],
			bruxism.CommandHelp(service, "customcomicsimple", "[id:] <text> | [id:] <text>", "Creates a simple custom comic.")[0],
			"Examples:",
			bruxism.CommandHelp(service, "comic", "5", "Creates a comic from the last 5 messages")[0],
			bruxism.CommandHelp(service, "customcomic", "A | B | C", "Creates a comic with 3 lines.")[0],
			bruxism.CommandHelp(service, "customcomic", "0: Hi! | 1: Hello! | 0: Goodbye.", "Creates a comic with 3 lines, the second line spoken by a different character")[0],
			bruxism.CommandHelp(service, "customcomic", "tiki: Hi! | jordy: Hello! | tiki: Goodbye.", "Creates a comic with 3 lines, containing tiki and jordy.")[0],
			bruxism.CommandHelp(service, "customcomicsimple", "0: Foo | 1: Bar", "Creates a comic with 2 lines, both spoken by different characters.")[0],
		}...)
	}

	return help
}
Exemple #22
0
// Help returns a list of help strings that are printed when the user requests them.
func (p *triviaPlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if service.IsPrivate(message) || !(service.IsModerator(message) || service.IsBotOwner(message)) {
		return nil
	}

	return []string{
		bruxism.CommandHelp(service, "trivia", "<start|stop> [theme]", "Starts or stops trivia with an optional theme.")[0],
		bruxism.CommandHelp(service, "trivia", "<score>", "Returns your current trivia score.")[0],
	}
}
Exemple #23
0
func emojiMessageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if service.Name() == bruxism.DiscordServiceName && !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "emoji", message) || bruxism.MatchesCommand(service, "hugemoji", message) {
			base := "emoji/twitter"
			if bruxism.MatchesCommand(service, "hugemoji", message) {
				base = "emoji/twitterhuge"
			}
			_, parts := bruxism.ParseCommand(service, message)
			if len(parts) == 1 {
				submatches := discordRegex.FindStringSubmatch(parts[0])
				if len(submatches) != 0 {
					h, err := http.Get("https://cdn.discordapp.com/emojis/" + submatches[1] + ".png")
					if err != nil {
						return
					}

					service.SendFile(message.Channel(), "emoji.png", h.Body)
					h.Body.Close()

					return

				}

				s := strings.TrimSpace(parts[0])
				for i := range s {
					filename := emojiFile(base, s[i:])
					if filename != "" {
						if f, err := os.Open(fmt.Sprintf("%s/%s.png", base, filename)); err == nil {
							defer f.Close()
							service.SendFile(message.Channel(), "emoji.png", f)

							return
						}
					}
				}
			}
		}
	}
}
Exemple #24
0
// NumberTriviaCommand is a command for getting number trivial.
func NumberTriviaCommand(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, command string, parts []string) {
	notfound := true
	num := rand.Intn(1000)
	if len(parts) == 1 {
		if i, err := strconv.Atoi(parts[0]); err == nil {
			num = i
			notfound = false
		}
	}

	service.Typing(message.Channel())

	str, err := numberTrivia(bot, num, notfound)
	if err != nil {
		service.SendMessage(message.Channel(), "There was an error requesting trivia, sorry!")
		return
	}

	service.SendMessage(message.Channel(), str)
}
Exemple #25
0
// InviteCommand is a command for accepting an invite to a channel.
func InviteCommand(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, command string, parts []string) {
	if service.Name() == bruxism.DiscordServiceName {
		discord := service.(*bruxism.Discord)

		if discord.ApplicationClientID != "" {
			service.SendMessage(message.Channel(), fmt.Sprintf("Please visit https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot to add %s to your server.", discord.ApplicationClientID, service.UserName()))
			return
		}
	}

	if len(parts) == 1 {
		join := parts[0]
		if service.Name() == bruxism.DiscordServiceName {
			join = discordInviteID(join)
		}
		if err := service.Join(join); err != nil {
			if service.Name() == bruxism.DiscordServiceName && err == bruxism.ErrAlreadyJoined {
				service.PrivateMessage(message.UserID(), "I have already joined that server.")
				return
			}
			log.Println("Error joining %s %v", service.Name(), err)
		} else if service.Name() == bruxism.DiscordServiceName {
			service.PrivateMessage(message.UserID(), "I have joined that server.")
		}
	}
}
Exemple #26
0
func (p *ReminderPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "remind", message) || bruxism.MatchesCommand(service, "reminder", message) {
			_, parts := bruxism.ParseCommand(service, message)

			if len(parts) < 2 {
				service.SendMessage(message.Channel(), fmt.Sprintf("Invalid reminder, no time or message. eg: %s", p.randomReminder(service)))
				return
			}

			t, r, err := p.parseReminder(parts)

			now := time.Now()

			if err != nil || t.Before(now) || t.After(now.Add(time.Hour*24*365+time.Hour)) {
				service.SendMessage(message.Channel(), fmt.Sprintf("Invalid time. eg: %s", strings.Join(randomTimes, ", ")))
				return
			}

			if r == "" {
				service.SendMessage(message.Channel(), fmt.Sprintf("Invalid reminder, no message. eg: %s", p.randomReminder(service)))
				return
			}

			requester := message.UserName()
			if service.Name() == bruxism.DiscordServiceName {
				requester = fmt.Sprintf("<@%s>", message.UserID())
			}

			err = p.AddReminder(&Reminder{
				StartTime: now,
				Time:      t,
				Requester: requester,
				Target:    message.Channel(),
				Message:   r,
				IsPrivate: service.IsPrivate(message),
			})
			if err != nil {
				service.SendMessage(message.Channel(), err.Error())
				return
			}

			service.SendMessage(message.Channel(), fmt.Sprintf("Reminder set for %s.", humanize.Time(t)))
		}
	}
}
Exemple #27
0
func (p *streamerPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "streamer", message) {
			query, parts := bruxism.ParseCommand(service, message)

			if len(parts) == 0 {
				return
			}

			r, _ := p.requests[query]
			if r == nil {
				r = &streamerPluginRequest{}
				p.requests[query] = r
			}

			n := time.Now()
			if !n.After(r.lastUpdate.Add(60 * time.Minute)) {
				if r.lastMessage != "" {
					service.SendMessage(message.Channel(), fmt.Sprintf("%s *Last updated %s.*", r.lastMessage, humanize.Time(r.lastUpdate)))
				}
				return
			}

			service.Typing(message.Channel())

			r.lastUpdate = n

			m, err := p.streamer(query, service.Name() == bruxism.DiscordServiceName)
			if err != nil {
				service.SendMessage(message.Channel(), "There was an error while requesting the streamer, please try again later.")
				return
			}

			service.SendMessage(message.Channel(), m)
			r.lastMessage = m
		}
	}
}
Exemple #28
0
// Message handler.
func (p *livePlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if !service.IsMe(message) {
		messageChannel := message.Channel()

		if bruxism.MatchesCommand(service, "live", message) {
			ticks := ""
			if service.Name() == bruxism.DiscordServiceName {
				ticks = "`"
			}

			_, parts := bruxism.ParseCommand(service, message)

			if len(parts) == 0 {
				service.SendMessage(messageChannel, fmt.Sprintf("Incorrect command. eg: %s%slive [add|remove|list] <%s>%s", ticks, service.CommandPrefix(), "UCGmC0A8mEAPdlELQdP9xJbw", ticks))
			}

			switch parts[0] {
			case "list":
				if !service.IsModerator(message) {
					service.SendMessage(messageChannel, "I'm sorry, you must be the channel owner to list live announcements.")
					return
				}
				list := []string{}
				p.Lock()
				for ytChannel := range p.ChannelToYouTubeChannels[messageChannel] {
					list = append(list, fmt.Sprintf("%s (%s)", p.ytLiveChannel.ChannelName(ytChannel), ytChannel))
				}
				p.Unlock()
				if len(list) == 0 {
					service.SendMessage(messageChannel, "No Channels are being announced.")
				} else {
					service.SendMessage(messageChannel, fmt.Sprintf("Currently announcing: %s", strings.Join(list, ",")))
				}
			case "add":
				if !service.IsModerator(message) {
					service.SendMessage(messageChannel, "I'm sorry, you must be the channel owner to add live announcements.")
					return
				}
				if len(parts) != 2 || len(parts[1]) != 24 {
					service.SendMessage(messageChannel, fmt.Sprintf("Incorrect Channel ID. eg: %s%slive %s %s%s", ticks, service.CommandPrefix(), parts[0], "UCGmC0A8mEAPdlELQdP9xJbw", ticks))
					return
				}
				err := p.monitor(messageChannel, parts[1])
				if err != nil {
					service.SendMessage(messageChannel, fmt.Sprintf("Could not add Channel ID. %s", err))
					return
				}
				service.SendMessage(messageChannel, fmt.Sprintf("Messages will be sent here when %s goes live.", p.ytLiveChannel.ChannelName(parts[1])))
			case "remove":
				if !service.IsModerator(message) {
					service.SendMessage(messageChannel, "I'm sorry, you must be the channel owner to remove live announcements.")
					return
				}
				if len(parts) != 2 || len(parts[1]) != 24 {
					service.SendMessage(messageChannel, fmt.Sprintf("Incorrect Channel ID. eg: %s%slive %s %s%s", ticks, service.CommandPrefix(), parts[0], "UCGmC0A8mEAPdlELQdP9xJbw", ticks))
					return
				}
				p.Lock()
				delete(p.ChannelToYouTubeChannels[messageChannel], parts[1])
				delete(p.youTubeChannelToChannels[parts[1]], messageChannel)
				p.Unlock()
				service.SendMessage(messageChannel, fmt.Sprintf("Messages will no longer be sent here when %s goes live.", p.ytLiveChannel.ChannelName(parts[1])))
			}
		}
	}
}
Exemple #29
0
func messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if service.IsMe(message) || !bruxism.MatchesCommand(service, "myson", message) || service.Name() != bruxism.DiscordServiceName {
		return
	}

	discord := service.(*bruxism.Discord)
	discord.Session.ChannelMessageSendEmbed(message.Channel(), &discordgo.MessageEmbed{
		Color:       discord.UserColor(service.UserID(), message.Channel()),
		Description: "Don't ever talk to me or my son ever again.",
		Author: &discordgo.MessageEmbedAuthor{
			Name:    discord.NicknameForID(service.UserID(), service.UserName(), message.Channel()),
			IconURL: discordgo.EndpointUserAvatar(service.UserID(), discord.Session.State.User.Avatar),
		},
	})
}
Exemple #30
0
func (p *comicPlugin) makeComic(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, script *comicgen.Script) {
	p.Comics++
	comic := comicgen.NewComicGen("arial", service.Name() != bruxism.DiscordServiceName)
	image, err := comic.MakeComic(script)
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was an error creating the comic. %s", message.UserName(), err))
	} else {
		go func() {
			if service.Name() == bruxism.DiscordServiceName {
				discord := service.(*bruxism.Discord)
				p, err := discord.UserChannelPermissions(message.UserID(), message.Channel())
				if err == nil && p&discordgo.PermissionAttachFiles != 0 {
					b := &bytes.Buffer{}
					err = png.Encode(b, image)
					if err != nil {
						service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your comic.", message.UserName()))
						return
					}

					if err := service.SendFile(message.Channel(), "comic.png", b); err == nil {
						return
					}
				}
			}

			b := &bytes.Buffer{}
			err = png.Encode(b, image)
			if err != nil {
				service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your comic.", message.UserName()))
				return
			}

			url, err := bot.UploadToImgur(b, "comic.png")
			if err != nil {
				service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem uploading the comic to imgur.", message.UserName()))
				log.Println("Error uploading comic: ", err)
				return
			}

			if service.Name() == bruxism.DiscordServiceName {
				service.SendMessage(message.Channel(), fmt.Sprintf("Here's your comic <@%s>: %s", message.UserID(), url))
			} else {
				service.SendMessage(message.Channel(), fmt.Sprintf("Here's your comic %s: %s", message.UserName(), url))
			}

			runtime.GC()
		}()
	}
}