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 } } }
// 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.") } } }
// 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) } } } }
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 } } }
// 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)) } }
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)) } } }
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 } } } } } } }
// Help returns a list of help strings that are printed when the user requests them. func (p *MusicPlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string { // Only show help messages for guilds where we have a voice connection c, err := p.discord.Channel(message.Channel()) if err != nil { log.Println("musicplugin: fetching channel err:", err.Error()) return nil } _, ok := p.VoiceConnections[c.GuildID] if !ok { return nil } help := []string{ bruxism.CommandHelp(service, "music", "<command>", "Music Plugin, see `help music`")[0], } if detailed { help = append(help, []string{ "Examples:", bruxism.CommandHelp(service, "music", "join [channelid]", "Join your voice channel or the provided voice channel.")[0], bruxism.CommandHelp(service, "music", "leave", "Leave current voice channel.")[0], bruxism.CommandHelp(service, "music", "play [url]", "Start playing music and optionally enqueue provided url.")[0], bruxism.CommandHelp(service, "music", "info", "Information about this plugin and the currently playing song.")[0], bruxism.CommandHelp(service, "music", "pause", "Pause playback of current song.")[0], bruxism.CommandHelp(service, "music", "resume", "Resume playback of current song.")[0], bruxism.CommandHelp(service, "music", "skip", "Skip current song.")[0], bruxism.CommandHelp(service, "music", "stop", "Stop playing music.")[0], bruxism.CommandHelp(service, "music", "list", "List contents of queue.")[0], bruxism.CommandHelp(service, "music", "clear", "Clear all items from queue.")[0], }...) } return help }
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), }, }) }
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 } } }
// 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) }
// Message handler. func (p *MusicPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) { defer bruxism.MessageRecover() if service.IsMe(message) { return } if !bruxism.MatchesCommand(service, "music", message) && !bruxism.MatchesCommand(service, "mu", message) { return } _, parts := bruxism.ParseCommand(service, message) if len(parts) == 0 { service.SendMessage(message.Channel(), strings.Join(p.Help(bot, service, message, true), "\n")) return } // Get the Channel (and GuildID) for this channel because it's needed in // a few locations below channel, err := p.discord.Channel(message.Channel()) if err != nil { log.Println("musicplugin: fetching channel err:", err.Error()) return } // grab pointer to this channels voice connection, if exists. vc, vcok := p.VoiceConnections[channel.GuildID] switch parts[0] { case "help": // display extended help information service.SendMessage(message.Channel(), strings.Join(p.Help(bot, service, message, true), "\n")) case "stats": // TODO: maybe provide plugin stats, total channels, total song queues, etc case "join": if !service.IsBotOwner(message) { service.SendMessage(message.Channel(), "Sorry, only bot owner can join, please ask him to run this command.") return } // join the voice channel of the caller or the provided channel ID channelID := "" if len(parts) > 1 { channelID = parts[1] } if channelID == "" { messageUserID := message.UserID() for _, g := range p.discord.Guilds() { for _, v := range g.VoiceStates { if v.UserID == messageUserID { channelID = v.ChannelID } } } if channelID == "" { service.SendMessage(message.Channel(), "I couldn't find you in any voice channels, please join one.") return } } _, err := p.join(channelID) if err != nil { service.SendMessage(message.Channel(), err.Error()) break } service.SendMessage(message.Channel(), "Now, let's play some music!") case "leave": // leave voice channel for this Guild if !service.IsBotOwner(message) { service.SendMessage(message.Channel(), "Sorry, only bot owner can leave, please ask him to run this command.") return } if !vcok { service.SendMessage(message.Channel(), "There is no voice connection for this Guild.") } vc.conn.Close() delete(p.VoiceConnections, channel.GuildID) service.SendMessage(message.Channel(), "Closed voice connection.") case "debug": // enable or disable debug if !vcok { service.SendMessage(message.Channel(), fmt.Sprintf("There is no voice connection for this Guild.")) } vc.Lock() vc.debug = !vc.debug service.SendMessage(message.Channel(), fmt.Sprintf("debug mode set to %v", vc.debug)) vc.Unlock() case "play": // Start queue player and optionally enqueue provided songs p.gostart(vc) for _, v := range parts[1:] { url, err := url.Parse(v) // doesn't check much.. if err != nil { continue } err = p.enqueue(vc, url.String(), service, message) if err != nil { // TODO: Might need improving. service.SendMessage(message.Channel(), err.Error()) } } case "stop": // stop the queue player if vc.close != nil { close(vc.close) vc.close = nil } if vc.control != nil { close(vc.control) vc.control = nil } case "skip": // skip current song if vc.control == nil { return } vc.control <- Skip case "pause": // pause the queue player if vc.control == nil { return } vc.control <- Pause case "resume": // resume the queue player if vc.control == nil { return } vc.control <- Resume case "info": // report player settings, queue info, and current song msg := fmt.Sprintf("`Bruxism MusicPlugin:`\n") msg += fmt.Sprintf("`Voice Channel:` %s\n", vc.ChannelID) msg += fmt.Sprintf("`Queue Size:` %d\n", len(vc.Queue)) if vc.playing == nil { service.SendMessage(message.Channel(), msg) break } msg += fmt.Sprintf("`Now Playing:`\n") msg += fmt.Sprintf("`ID:` %s\n", vc.playing.ID) msg += fmt.Sprintf("`Title:` %s\n", vc.playing.Title) msg += fmt.Sprintf("`Duration:` %ds\n", vc.playing.Duration) msg += fmt.Sprintf("`Remaining:` %ds\n", vc.playing.Remaining) msg += fmt.Sprintf("`Source URL:` <%s>\n", vc.playing.URL) msg += fmt.Sprintf("`Thumbnail:` %s\n", vc.playing.Thumbnail) service.SendMessage(message.Channel(), msg) case "list": // list top items in the queue if len(vc.Queue) == 0 { service.SendMessage(message.Channel(), "The music queue is empty.") return } var msg string i := 1 i2 := 0 for k, v := range vc.Queue { np := "" if vc.playing != nil && *vc.playing == v { np = "**(Now Playing)**" } d := time.Duration(v.Duration) * time.Second msg += fmt.Sprintf("`%.3d:%.15s` **%s** [%s] - *%s* %s\n", k, v.ID, v.Title, d.String(), v.AddedBy, np) if i >= 15 { service.SendMessage(message.Channel(), msg) msg = "" i = 0 i2++ if i2 >= 8 { // limit response to 8 messages (120 songs) return } } i++ } service.SendMessage(message.Channel(), msg) case "clear": // clear all items from the queue vc.Lock() vc.Queue = []song{} vc.Unlock() default: service.SendMessage(message.Channel(), "Unknown music command, try `help music`") } }
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))) } } }
// Message handler. func (p *triviaPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) { defer bruxism.MessageRecover() if !service.IsMe(message) && !service.IsPrivate(message) { messageChannel := message.Channel() isCommand := bruxism.MatchesCommand(service, "trivia", message) if isCommand && (service.IsModerator(message) || service.IsBotOwner(message)) { p.Lock() tc := p.Channels[messageChannel] if tc == nil { tc = &triviaChannel{ Channel: messageChannel, Scores: map[string]*triviaScore{}, } p.Channels[messageChannel] = tc } p.Unlock() _, parts := bruxism.ParseCommand(service, message) if len(parts) == 0 { return } switch parts[0] { case "start": theme := "" if len(parts) >= 2 { theme = parts[1] } tc.Start(bot, service, theme) case "stop": tc.Stop(bot, service) } } else { if isCommand { _, parts := bruxism.ParseCommand(service, message) if len(parts) == 0 { return } if parts[0] == "score" { p.RLock() tc := p.Channels[messageChannel] if tc != nil { ts := tc.Scores[message.UserID()] if ts != nil { service.SendMessage(message.Channel(), fmt.Sprintf("%s's score is %d.", message.UserName(), ts.Score)) } else { service.SendMessage(message.Channel(), fmt.Sprintf("%s's score is 0.", message.UserName())) } } p.RUnlock() } return } p.RLock() tc := p.Channels[messageChannel] p.RUnlock() if tc != nil { tc.Message(bot, service, message) } } } }
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() }() } }
// Message handler. func (p *comicPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) { if service.IsMe(message) { return } p.Lock() defer p.Unlock() log, ok := p.log[message.Channel()] if !ok { log = []bruxism.Message{} } if bruxism.MatchesCommand(service, "customcomic", message) || bruxism.MatchesCommand(service, "customcomicsimple", message) { ty := comicgen.ComicTypeChat if bruxism.MatchesCommand(service, "customcomicsimple", message) { ty = comicgen.ComicTypeSimple } service.Typing(message.Channel()) str, _ := bruxism.ParseCommand(service, message) messages := []*comicgen.Message{} splits := strings.Split(str, "|") for _, line := range splits { line := strings.Trim(line, " ") text := "" speaker := 0 author := "" if strings.Index(line, ":") != -1 { lineSplit := strings.Split(line, ":") author = strings.ToLower(strings.Trim(lineSplit[0], " ")) var err error speaker, err = strconv.Atoi(author) if err != nil { speaker = -1 } text = strings.Trim(lineSplit[1], " ") } else { text = line } messages = append(messages, &comicgen.Message{ Speaker: speaker, Text: text, Author: author, }) } if len(messages) == 0 { service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, you didn't add any text.", message.UserName())) return } p.makeComic(bot, service, message, &comicgen.Script{ Messages: messages, Author: fmt.Sprintf(service.UserName()), Type: ty, }) } else if bruxism.MatchesCommand(service, "comic", message) { if len(log) == 0 { service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, I don't have enough messages to make a comic yet.", message.UserName())) return } service.Typing(message.Channel()) lines := 0 linesString, parts := bruxism.ParseCommand(service, message) if len(parts) > 0 { lines, _ = strconv.Atoi(linesString) } if lines <= 0 { lines = 1 + int(math.Floor((math.Pow(2*rand.Float64()-1, 3)/2+0.5)*float64(5))) } if lines > len(log) { lines = len(log) } p.makeComic(bot, service, message, makeScriptFromMessages(service, message, log[len(log)-lines:])) } else { // Don't append commands. if bruxism.MatchesCommand(service, "", message) { return } switch message.Type() { case bruxism.MessageTypeCreate: if len(log) < 10 { log = append(log, message) } else { log = append(log[1:], message) } case bruxism.MessageTypeUpdate: for i, m := range log { if m.MessageID() == message.MessageID() { log[i] = message break } } case bruxism.MessageTypeDelete: for i, m := range log { if m.MessageID() == message.MessageID() { log = append(log[:i], log[i+1:]...) break } } } p.log[message.Channel()] = log } }
func (p *chartPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) { if service.IsMe(message) { return } if bruxism.MatchesCommand(service, "chart", message) { query, parts := bruxism.ParseCommand(service, message) if len(parts) == 0 { service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart eg: %s", p.randomChart(service))) return } start, end := 0.5, 0.5 switch parts[0] { case "up": start, end = 0, 1 case "down": start, end = 1, 0 case "flat": case "straight": default: service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart direction. eg: %s", p.randomChart(service))) return } axes := strings.Split(query[len(parts[0]):], ",") if len(axes) != 2 { service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart axis labels eg: %s", p.randomChart(service))) return } pl, err := plot.New() if err != nil { service.SendMessage(message.Channel(), fmt.Sprintf("Error making chart, sorry! eg: %s", p.randomChart(service))) return } service.Typing(message.Channel()) pl.Y.Label.Text = axes[0] pl.X.Label.Text = axes[1] num := 5 + rand.Intn(15) start *= float64(num) end *= float64(num) pts := make(plotter.XYs, num) for i := range pts { pts[i].X = float64(i) + rand.Float64()*0.5 - 0.2 pts[i].Y = start + float64(end-start)/float64(num-1)*float64(i) + rand.Float64()*0.5 - 0.25 } pl.X.Tick.Label.Color = color.Transparent pl.Y.Tick.Label.Color = color.Transparent pl.X.Min = -0.5 pl.X.Max = float64(num) + 0.5 pl.Y.Min = -0.5 pl.Y.Max = float64(num) + 0.5 lpLine, lpPoints, err := plotter.NewLinePoints(pts) if err != nil { service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your chart.", message.UserName())) } lpLine.Color = plotutil.Color(rand.Int()) lpLine.Width = vg.Points(1 + 0.5*rand.Float64()) lpLine.Dashes = plotutil.Dashes(rand.Int()) lpPoints.Shape = plotutil.Shape(rand.Int()) lpPoints.Color = lpLine.Color pl.Add(lpLine, lpPoints) w, err := pl.WriterTo(320, 240, "png") if err != nil { service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your chart.", message.UserName())) return } b := &bytes.Buffer{} w.WriteTo(b) 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 { service.SendFile(message.Channel(), "chart.png", b) return } } url, err := bot.UploadToImgur(b, "chart.png") if err != nil { service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem uploading the chart to imgur.", message.UserName())) log.Println("Error uploading chart: ", err) return } if service.Name() == bruxism.DiscordServiceName { service.SendMessage(message.Channel(), fmt.Sprintf("Here's your chart <@%s>: %s", message.UserID(), url)) } else { service.SendMessage(message.Channel(), fmt.Sprintf("Here's your chart %s: %s", message.UserName(), url)) } }() } }
// StatsCommand returns bot statistics. func StatsCommand(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, command string, parts []string) { stats := runtime.MemStats{} runtime.ReadMemStats(&stats) w := &tabwriter.Writer{} buf := &bytes.Buffer{} w.Init(buf, 0, 4, 0, ' ', 0) if service.Name() == bruxism.DiscordServiceName { fmt.Fprintf(w, "```\n") } fmt.Fprintf(w, "Bruxism: \t%s\n", bruxism.VersionString) if service.Name() == bruxism.DiscordServiceName { fmt.Fprintf(w, "Discordgo: \t%s\n", discordgo.VERSION) } fmt.Fprintf(w, "Go: \t%s\n", runtime.Version()) fmt.Fprintf(w, "Uptime: \t%s\n", getDurationString(time.Now().Sub(statsStartTime))) fmt.Fprintf(w, "Memory used: \t%s / %s (%s garbage collected)\n", humanize.Bytes(stats.Alloc), humanize.Bytes(stats.Sys), humanize.Bytes(stats.TotalAlloc)) fmt.Fprintf(w, "Concurrent tasks: \t%d\n", runtime.NumGoroutine()) if service.Name() == bruxism.DiscordServiceName { discord := service.(*bruxism.Discord) fmt.Fprintf(w, "Connected servers: \t%d\n", service.ChannelCount()) if len(discord.Sessions) > 1 { shards := 0 for _, s := range discord.Sessions { if s.DataReady { shards++ } } if shards == len(discord.Sessions) { fmt.Fprintf(w, "Shards: \t%d\n", shards) } else { fmt.Fprintf(w, "Shards: \t%d (%d connected)\n", len(discord.Sessions), shards) } guild, err := discord.Channel(message.Channel()) if err == nil { id, err := strconv.Atoi(guild.ID) if err == nil { fmt.Fprintf(w, "Current shard: \t%d\n", ((id>>22)%len(discord.Sessions) + 1)) } } } } else { fmt.Fprintf(w, "Connected channels: \t%d\n", service.ChannelCount()) } plugins := bot.Services[service.Name()].Plugins names := []string{} for _, plugin := range plugins { names = append(names, plugin.Name()) sort.Strings(names) } for _, name := range names { stats := plugins[name].Stats(bot, service, message) for _, stat := range stats { fmt.Fprint(w, stat) } } if service.Name() == bruxism.DiscordServiceName { fmt.Fprintf(w, "\n```") } w.Flush() out := buf.String() end := "" if IsSeptapus { end += "Septapus community: <https://discord.gg/HWN9pwj>\nPatreon: <https://www.patreon.com/iopred>\nBuilt with love by iopred." } if service.SupportsMultiline() { if end != "" { out += "\n" + end } service.SendMessage(message.Channel(), out) } else { service.SendMessage(message.Channel(), strings.Join(strings.Split(out, "\n"), " ")) service.SendMessage(message.Channel(), strings.Join(strings.Split(end, "\n"), " ")) } }
// 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]))) } } } }
// enqueue a song/playlest to a VoiceConnections Queue func (p *MusicPlugin) enqueue(vc *voiceConnection, url string, service bruxism.Service, message bruxism.Message) (err error) { if vc == nil { return fmt.Errorf("Cannot enqueue to nil voice connection.") } if url == "" { return fmt.Errorf("Cannot enqueue an empty string.") } // TODO ////////////////////////////////////////////////////////////////// // need to parse the url and have a way to know what we're doing // 1) option to queue local files // 2) option to queue saved playlists // 3) option to queue URL that can be passed directly to ffmpeg without youtube-dl // 4) option to queue youtube-dl playlist // 5) option to queue youtube-dl song // 6) option to queue youtube-dl search result // right now option 4 and 5 work, only. ////////////////////////////////////////////////////////////////////////// cmd := exec.Command("./youtube-dl", "-i", "-j", "--youtube-skip-dash-manifest", url) if vc.debug { cmd.Stderr = os.Stderr } output, err := cmd.StdoutPipe() if err != nil { log.Println(err) service.SendMessage(message.Channel(), fmt.Sprintf("Error adding song to playlist.")) return } err = cmd.Start() if err != nil { log.Println(err) service.SendMessage(message.Channel(), fmt.Sprintf("Error adding song to playlist.")) return } defer func() { go cmd.Wait() }() scanner := bufio.NewScanner(output) for scanner.Scan() { s := song{} err = json.Unmarshal(scanner.Bytes(), &s) if err != nil { log.Println(err) continue } s.AddedBy = message.UserName() vc.Lock() vc.Queue = append(vc.Queue, s) vc.Unlock() } return }
func (p *playedPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) { defer bruxism.MessageRecover() if service.Name() == bruxism.DiscordServiceName && !service.IsMe(message) { if bruxism.MatchesCommand(service, "played", message) { query := strings.Join(strings.Split(message.RawMessage(), " ")[1:], " ") id := message.UserID() match := userIDRegex.FindStringSubmatch(query) if match != nil { id = match[1] } p.Lock() defer p.Unlock() u := p.Users[id] if u == nil { service.SendMessage(message.Channel(), "I haven't seen that user.") return } if len(u.Entries) == 0 { service.SendMessage(message.Channel(), "I haven't seen anything played by that user.") return } lc := humanize.Time(u.LastChanged) u.Update(u.Current, time.Now()) pes := make(byDuration, len(u.Entries)) i := 0 for _, pe := range u.Entries { pes[i] = pe i++ } sort.Sort(pes) messageText := fmt.Sprintf("*First seen %s, last update %s*\n", humanize.Time(u.FirstSeen), lc) for i = 0; i < len(pes) && i < 5; i++ { pe := pes[i] du := pe.Duration ds := "" hours := int(du / time.Hour) if hours > 0 { ds += fmt.Sprintf("%dh ", hours) du -= time.Duration(hours) * time.Hour } minutes := int(du / time.Minute) if minutes > 0 || len(ds) > 0 { ds += fmt.Sprintf("%dm ", minutes) du -= time.Duration(minutes) * time.Minute } seconds := int(du / time.Second) ds += fmt.Sprintf("%ds", seconds) messageText += fmt.Sprintf("**%s**: %s\n", pe.Name, ds) } service.SendMessage(message.Channel(), messageText) } } }