Skip to content

Instantly share code, notes, and snippets.

@0x263b
Last active September 24, 2018 04:35
Show Gist options
  • Save 0x263b/2d2b7a37a675750cc696aec41f69226a to your computer and use it in GitHub Desktop.
Save 0x263b/2d2b7a37a675750cc696aec41f69226a to your computer and use it in GitHub Desktop.
Discord spoiler bot
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"os/signal"
"regexp"
"runtime"
"strings"
"syscall"
"time"
"github.com/bwmarrin/discordgo"
humanize "github.com/dustin/go-humanize"
)
// Add your discord token
// and your bot Client ID
const (
Token string = ""
ClientID string = ""
BotName string = "Spoiler Bot"
EmbedColor int = 0x01ffe1
)
type Pasted struct {
// Struct for paste json response
Paste struct {
ID string `json:"id"`
Link string `json:"link"`
Raw string `json:"raw"`
LangCode string `json:"lang_code"`
Formatted string `json:"formatted"`
ExpirationDate time.Time `json:"expiration_date"`
} `json:"paste"`
Status string `json:"status"`
}
func pasteURL(title string, paste string) string {
form := url.Values{
"title": {title},
"lang": {"markdown"},
"paste": {paste},
}
encoded := bytes.NewBufferString(form.Encode())
client := &http.Client{}
request, _ := http.NewRequest("POST", "https://mnn.im/c", encoded)
response, _ := client.Do(request)
defer response.Body.Close()
body, _ := ioutil.ReadAll(response.Body)
var pasted Pasted
json.Unmarshal(body, &pasted)
return pasted.Paste.Formatted
}
func main() {
// Create a new Discord session using the provided bot token.
dg, err := discordgo.New("Bot " + Token)
if err != nil {
fmt.Println("error creating Discord session,", err)
return
}
// Register the messageCreate func as a callback for MessageCreate events.
dg.AddHandler(messageCreate)
// Open a websocket connection to Discord and begin listening.
err = dg.Open()
if err != nil {
fmt.Println("error opening connection,", err)
return
}
// Wait here until CTRL-C or other term signal is received.
fmt.Println("Bot is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc
// Cleanly close down the Discord session.
dg.Close()
}
// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the authenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// Ignore all messages created by the bot itself
// This isn't required in this specific example but it's a good practice.
if m.Author.ID == s.State.User.ID {
return
}
message := strings.ToLower(m.Content)
// Spoiler command
cw := regexp.MustCompile(`^!(sp|cw) (.+)\|(.+)`) // has warning
nw := regexp.MustCompile(`^!(sp|cw) (.+)`) // no warning
if cw.MatchString(message) {
cmdSpoiler(s, m, true)
} else if nw.MatchString(message) {
cmdSpoiler(s, m, false)
}
if message == "!help" {
cmdHelp(s, m)
}
if message == "!stats" {
cmdStats(s, m)
}
if message == "!ping" {
s.ChannelMessageSend(m.ChannelID, "Pong!")
}
}
func cmdSpoiler(s *discordgo.Session, m *discordgo.MessageCreate, hasWarn bool) {
s.ChannelMessageDelete(m.ChannelID, m.ID)
var spoiler string
spoiler = strings.Replace(m.Content, "!sp ", "", 1)
spoiler = strings.Replace(spoiler, "!cw ", "", 1)
var warning string
var content string
if hasWarn {
split := strings.SplitN(spoiler, "|", 2)
warning = split[0]
content = strings.TrimSpace(split[1])
} else {
warning = "Content Warning"
content = spoiler
}
rotated := rot13(content)
if len(rotated) > 240 {
rotated = fmt.Sprintf("%s...", rotated[0:240])
}
markdown := fmt.Sprintf("### %s \n\nPosted by `%s` \n\n%s", warning, m.Author.Username, content)
markdownURL := pasteURL(warning, markdown)
s.ChannelMessageSendEmbed(m.ChannelID, &discordgo.MessageEmbed{
Author: &discordgo.MessageEmbedAuthor{
Name: m.Author.Username,
IconURL: m.Author.AvatarURL(""),
},
Color: EmbedColor,
Title: warning,
Description: rotated,
URL: markdownURL,
})
}
func cmdHelp(s *discordgo.Session, m *discordgo.MessageCreate) {
s.ChannelMessageSendEmbed(m.ChannelID, &discordgo.MessageEmbed{
Author: &discordgo.MessageEmbedAuthor{
Name: BotName,
IconURL: s.State.User.AvatarURL("128"),
URL: fmt.Sprintf("https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=92160", ClientID),
},
Color: EmbedColor,
Fields: []*discordgo.MessageEmbedField{
{
Name: "Usage",
Value: "```!sp your message```",
Inline: false,
},
{
Name: "Custom warning message",
Value: "```!sp spoiler for star warps | your message```",
Inline: false,
},
},
})
}
func cmdStats(s *discordgo.Session, m *discordgo.MessageCreate) {
stats := runtime.MemStats{}
runtime.ReadMemStats(&stats)
users := 0
for _, guild := range s.State.Ready.Guilds {
users += len(guild.Members)
}
channels := 0
for _, guild := range s.State.Ready.Guilds {
channels += len(guild.Channels)
}
s.ChannelMessageSendEmbed(m.ChannelID, &discordgo.MessageEmbed{
Author: &discordgo.MessageEmbedAuthor{
Name: BotName,
IconURL: s.State.User.AvatarURL("128"),
URL: fmt.Sprintf("https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=92160", ClientID),
},
Color: EmbedColor,
Fields: []*discordgo.MessageEmbedField{
{
Name: "Discordgo",
Value: discordgo.VERSION,
Inline: true,
},
{
Name: "Go",
Value: runtime.Version(),
Inline: true,
},
{
Name: "Memory",
Value: fmt.Sprintf("%s / %s", humanize.Bytes(stats.Alloc), humanize.Bytes(stats.Sys)),
Inline: true,
},
{
Name: "Tasks",
Value: fmt.Sprintf("%d", runtime.NumGoroutine()),
Inline: true,
},
{
Name: "Servers",
Value: fmt.Sprintf("%d", len(s.State.Ready.Guilds)),
Inline: true,
},
{
Name: "Channels",
Value: fmt.Sprintf("%d", channels),
Inline: true,
},
{
Name: "Users",
Value: fmt.Sprintf("%d", users),
Inline: true,
},
},
})
}
func rot13char(c rune) rune {
if c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M' {
return c + 13
} else if c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z' {
return c - 13
}
return c
}
func rot13(s string) string {
return strings.Map(rot13char, s)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment