Compare commits

..

No commits in common. "1acb78bb5acccb80bf7d4e3628a84abf8772cf01" and "92da19e5a8fc8974010abf2baef659f3f9ee57ae" have entirely different histories.

19 changed files with 115 additions and 234 deletions

View File

@ -61,8 +61,7 @@ type bot struct {
} }
type EndPoint struct { type EndPoint struct {
Name string `json:"name"` Name, URL string
URL string `json:"url"`
} }
// Variable represents a $var replacement // Variable represents a $var replacement
@ -105,7 +104,6 @@ func New(config *config.Config, connector Connector) Bot {
bot.RefreshPluginWhitelist() bot.RefreshPluginWhitelist()
http.HandleFunc("/", bot.serveRoot) http.HandleFunc("/", bot.serveRoot)
http.HandleFunc("/nav", bot.serveNav)
connector.RegisterEvent(bot.Receive) connector.RegisterEvent(bot.Receive)
@ -182,6 +180,10 @@ var suffixRegex *regexp.Regexp
func IsCmd(c *config.Config, message string) (bool, string) { func IsCmd(c *config.Config, message string) (bool, string) {
cmdcs := c.GetArray("CommandChar", []string{"!"}) cmdcs := c.GetArray("CommandChar", []string{"!"})
botnick := strings.ToLower(c.Get("Nick", "bot")) botnick := strings.ToLower(c.Get("Nick", "bot"))
r := fmt.Sprintf(`(?i)\s*\W*\s*?%s\W*$`, botnick)
if suffixRegex == nil {
suffixRegex = regexp.MustCompile(r)
}
if botnick == "" { if botnick == "" {
log.Fatal(). log.Fatal().
Msgf(`You must run catbase -set nick -val <your bot nick>`) Msgf(`You must run catbase -set nick -val <your bot nick>`)
@ -200,6 +202,9 @@ func IsCmd(c *config.Config, message string) (bool, string) {
if message[0] == ':' || message[0] == ',' { if message[0] == ':' || message[0] == ',' {
message = message[1:] message = message[1:]
} }
} else if suffixRegex.MatchString(message) {
iscmd = true
message = suffixRegex.ReplaceAllString(message, "")
} else { } else {
for _, cmdc := range cmdcs { for _, cmdc := range cmdcs {
if strings.HasPrefix(lowerMessage, cmdc) && len(cmdc) > 0 { if strings.HasPrefix(lowerMessage, cmdc) && len(cmdc) > 0 {

View File

@ -37,8 +37,6 @@ const (
type EphemeralID string type EphemeralID string
type UnfurlLinks bool
type ImageAttachment struct { type ImageAttachment struct {
URL string URL string
AltTxt string AltTxt string

View File

@ -1,24 +1,16 @@
package bot package bot
import ( import (
"encoding/json" "html/template"
"fmt"
"net/http" "net/http"
"strings" "strings"
) )
func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) { func (b *bot) serveRoot(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, rootIndex) context := make(map[string]interface{})
} context["Nav"] = b.GetWebNavigation()
t := template.Must(template.New("rootIndex").Parse(rootIndex))
func (b *bot) serveNav(w http.ResponseWriter, r *http.Request) { t.Execute(w, context)
enc := json.NewEncoder(w)
err := enc.Encode(b.GetWebNavigation())
if err != nil {
jsonErr, _ := json.Marshal(err)
w.WriteHeader(500)
w.Write(jsonErr)
}
} }
// GetWebNavigation returns a list of bootstrap-vue <b-nav-item> links // GetWebNavigation returns a list of bootstrap-vue <b-nav-item> links
@ -62,7 +54,7 @@ var rootIndex = `
<b-navbar> <b-navbar>
<b-navbar-brand>catbase</b-navbar-brand> <b-navbar-brand>catbase</b-navbar-brand>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url">{{ item.name }}</b-nav-item> <b-nav-item v-for="item in nav" :href="item.URL">{{ "{{ item.Name }}" }}</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
</b-navbar> </b-navbar>
</div> </div>
@ -72,15 +64,8 @@ var rootIndex = `
el: '#app', el: '#app',
data: { data: {
err: '', err: '',
nav: [], nav: {{ .Nav }},
}, },
mounted: function() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
}
}) })
</script> </script>
</body> </body>

View File

@ -281,12 +281,6 @@ func (s *SlackApp) sendMessage(channel, message string, meMessage bool, args ...
ImageURL: a.URL, ImageURL: a.URL,
Text: a.AltTxt, Text: a.AltTxt,
}) })
case bot.UnfurlLinks:
if a {
options = append(options, slack.MsgOptionEnableLinkUnfurl())
} else {
options = append(options, slack.MsgOptionDisableLinkUnfurl())
}
} }
} }
} }

View File

@ -5,6 +5,7 @@ package admin
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
@ -327,8 +328,10 @@ func (p *AdminPlugin) registerWeb() {
p.bot.RegisterWeb("/vars", "Variables") p.bot.RegisterWeb("/vars", "Variables")
} }
var tpl = template.Must(template.New("factoidIndex").Parse(varIndex))
func (p *AdminPlugin) handleWeb(w http.ResponseWriter, r *http.Request) { func (p *AdminPlugin) handleWeb(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, varIndex) tpl.Execute(w, struct{ Nav []bot.EndPoint }{p.bot.GetWebNavigation()})
} }
func (p *AdminPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) { func (p *AdminPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) {

View File

@ -24,7 +24,7 @@ var varIndex = `
<b-navbar> <b-navbar>
<b-navbar-brand>Variables</b-navbar-brand> <b-navbar-brand>Variables</b-navbar-brand>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Variables'">{{ item.name }}</b-nav-item> <b-nav-item v-for="item in nav" :href="item.URL" :active="item.Name === 'Variables'">{{ "{{ item.Name }}" }}</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
</b-navbar> </b-navbar>
<b-alert <b-alert
@ -32,7 +32,7 @@ var varIndex = `
variant="error" variant="error"
v-if="err" v-if="err"
@dismissed="err = ''"> @dismissed="err = ''">
{{ err }} {{ "{{ err }}" }}
</b-alert> </b-alert>
<b-container> <b-container>
<b-table <b-table
@ -48,7 +48,7 @@ var varIndex = `
el: '#app', el: '#app',
data: { data: {
err: '', err: '',
nav: [], nav: {{ .Nav }},
vars: [], vars: [],
sortBy: 'key', sortBy: 'key',
fields: [ fields: [
@ -58,11 +58,6 @@ var varIndex = `
}, },
mounted() { mounted() {
this.getData(); this.getData();
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
}, },
methods: { methods: {
getData: function() { getData: function() {

View File

@ -5,6 +5,7 @@ package cli
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"time" "time"
@ -90,8 +91,10 @@ func (p *CliPlugin) handleWebAPI(w http.ResponseWriter, r *http.Request) {
w.Write(data) w.Write(data)
} }
var tpl = template.Must(template.New("factoidIndex").Parse(indexHTML))
func (p *CliPlugin) handleWeb(w http.ResponseWriter, r *http.Request) { func (p *CliPlugin) handleWeb(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, indexHTML) tpl.Execute(w, struct{ Nav []bot.EndPoint }{p.bot.GetWebNavigation()})
} }
// Completing the Connector interface, but will not actually be a connector // Completing the Connector interface, but will not actually be a connector

View File

@ -24,14 +24,14 @@ var indexHTML = `
<b-navbar> <b-navbar>
<b-navbar-brand>CLI</b-navbar-brand> <b-navbar-brand>CLI</b-navbar-brand>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'CLI'">{{ item.name }}</b-nav-item> <b-nav-item v-for="item in nav" :href="item.URL" :active="item.Name === 'CLI'">{{ "{{ item.Name }}" }}</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
</b-navbar> </b-navbar>
<b-alert <b-alert
dismissable dismissable
variant="error" variant="error"
:show="err"> :show="err">
{{ err }} {{ "{{ err }}" }}
</b-alert> </b-alert>
<b-container> <b-container>
<b-row> <b-row>
@ -80,20 +80,13 @@ var indexHTML = `
el: '#app', el: '#app',
data: { data: {
err: '', err: '',
nav: [], nav: {{ .Nav }},
answer: '', answer: '',
correct: 0, correct: 0,
textarea: [], textarea: [],
user: '', user: '',
input: '', input: '',
}, },
mounted: function() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
},
computed: { computed: {
authenticated: function() { authenticated: function() {
if (this.user !== '') if (this.user !== '')

View File

@ -4,6 +4,7 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"math/rand" "math/rand"
"net/http" "net/http"
"regexp" "regexp"
@ -625,8 +626,10 @@ func (p *CounterPlugin) registerWeb() {
p.Bot.RegisterWeb("/counter", "Counter") p.Bot.RegisterWeb("/counter", "Counter")
} }
var tpl = template.Must(template.New("factoidIndex").Parse(html))
func (p *CounterPlugin) handleCounter(w http.ResponseWriter, r *http.Request) { func (p *CounterPlugin) handleCounter(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, html) tpl.Execute(w, struct{ Nav []bot.EndPoint }{p.Bot.GetWebNavigation()})
} }
func (p *CounterPlugin) handleCounterAPI(w http.ResponseWriter, r *http.Request) { func (p *CounterPlugin) handleCounterAPI(w http.ResponseWriter, r *http.Request) {

View File

@ -22,14 +22,14 @@ var html = `
<b-navbar> <b-navbar>
<b-navbar-brand>Counters</b-navbar-brand> <b-navbar-brand>Counters</b-navbar-brand>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Counter'">{{ item.name }}</b-nav-item> <b-nav-item v-for="item in nav" :href="item.URL" :active="item.Name === 'Counter'">{{ "{{ item.Name }}" }}</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
</b-navbar> </b-navbar>
<b-alert <b-alert
dismissable dismissable
:show="err" :show="err"
variant="error"> variant="error">
{{ err }} {{ "{{ err }}" }}
</b-alert> </b-alert>
<b-container> <b-container>
<b-row> <b-row>
@ -37,14 +37,14 @@ var html = `
<b-col><b-input v-model="answer"></b-col> <b-col><b-input v-model="answer"></b-col>
</b-row> </b-row>
<b-row v-for="(counter, user) in counters"> <b-row v-for="(counter, user) in counters">
{{ user }}: {{ "{{ user }}" }}:
<b-container> <b-container>
<b-row v-for="(count, thing) in counter"> <b-row v-for="(count, thing) in counter">
<b-col offset="1"> <b-col offset="1">
{{ thing }}: {{ "{{ thing }}" }}:
</b-col> </b-col>
<b-col> <b-col>
{{ count }} {{ "{{ count }}" }}
</b-col> </b-col>
<b-col cols="2"> <b-col cols="2">
<button @click="subtract(user,thing,count)">-</button> <button @click="subtract(user,thing,count)">-</button>
@ -72,17 +72,12 @@ var html = `
el: '#app', el: '#app',
data: { data: {
err: '', err: '',
nav: [], nav: {{ .Nav }},
answer: '', answer: '',
correct: 0, correct: 0,
counters: {} counters: {}
}, },
mounted() { mounted() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
axios.get('/counter/api') axios.get('/counter/api')
.then(resp => (this.counters = convertData(resp.data))) .then(resp => (this.counters = convertData(resp.data)))
.catch(err => (this.err = err)); .catch(err => (this.err = err));

View File

@ -842,6 +842,8 @@ func (p *FactoidPlugin) serveAPI(w http.ResponseWriter, r *http.Request) {
w.Write(data) w.Write(data)
} }
var tpl = template.Must(template.New("factoidIndex").Parse(factoidIndex))
func (p *FactoidPlugin) serveQuery(w http.ResponseWriter, r *http.Request) { func (p *FactoidPlugin) serveQuery(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, factoidIndex) tpl.Execute(w, struct{ Nav []bot.EndPoint }{p.Bot.GetWebNavigation()})
} }

View File

@ -32,7 +32,7 @@ var factoidIndex = `
<b-navbar> <b-navbar>
<b-navbar-brand>Factoids</b-navbar-brand> <b-navbar-brand>Factoids</b-navbar-brand>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Factoid'">{{ item.name }}</b-nav-item> <b-nav-item v-for="item in nav" :href="item.URL" :active="item.Name === 'Factoid'">{{ "{{ item.Name }}" }}</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
</b-navbar> </b-navbar>
<b-alert <b-alert
@ -40,7 +40,7 @@ var factoidIndex = `
variant="error" variant="error"
v-if="err" v-if="err"
@dismissed="err = ''"> @dismissed="err = ''">
{{ err }} {{ "{{ err }}" }}
</b-alert> </b-alert>
<b-form @submit="runQuery"> <b-form @submit="runQuery">
<b-container> <b-container>
@ -74,7 +74,7 @@ var factoidIndex = `
router, router,
data: { data: {
err: '', err: '',
nav: [], nav: {{ .Nav }},
query: '', query: '',
results: [], results: [],
fields: [ fields: [
@ -85,11 +85,6 @@ var factoidIndex = `
] ]
}, },
mounted() { mounted() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
if (this.$route.query.query) { if (this.$route.query.query) {
this.query = this.$route.query.query; this.query = this.$route.query.query;
this.runQuery() this.runQuery()

View File

@ -80,4 +80,5 @@ func (p *GitPlugin) registerWeb() {
http.HandleFunc("/git/gitea/event", p.giteaEvent) http.HandleFunc("/git/gitea/event", p.giteaEvent)
http.HandleFunc("/git/github/event", p.githubEvent) http.HandleFunc("/git/github/event", p.githubEvent)
http.HandleFunc("/git/gitlab/event", p.gitlabEvent) http.HandleFunc("/git/gitlab/event", p.gitlabEvent)
p.b.RegisterWeb("/git", "Git")
} }

View File

@ -44,6 +44,6 @@ func (p *GitPlugin) giteaEvent(w http.ResponseWriter, r *http.Request) {
chs := p.c.GetArray(fmt.Sprintf("gitea.%s.%s.channels", org, repo), []string{}) chs := p.c.GetArray(fmt.Sprintf("gitea.%s.%s.channels", org, repo), []string{})
for _, ch := range chs { for _, ch := range chs {
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg, bot.UnfurlLinks(false)) p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
} }
} }

View File

@ -145,6 +145,6 @@ func (p *GitPlugin) githubEvent(w http.ResponseWriter, r *http.Request) {
chs := p.c.GetArray(fmt.Sprintf("github.%s.%s.channels", owner, repo), []string{}) chs := p.c.GetArray(fmt.Sprintf("github.%s.%s.channels", owner, repo), []string{})
for _, ch := range chs { for _, ch := range chs {
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg, bot.UnfurlLinks(false)) p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
} }
} }

View File

@ -53,6 +53,6 @@ func (p *GitPlugin) gitlabEvent(w http.ResponseWriter, r *http.Request) {
} }
chs := p.c.GetArray(fmt.Sprintf("gitlab.%s.channels", owner), []string{}) chs := p.c.GetArray(fmt.Sprintf("gitlab.%s.channels", owner), []string{})
for _, ch := range chs { for _, ch := range chs {
p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg, bot.UnfurlLinks(false)) p.b.Send(p.b.DefaultConnector(), bot.Message, ch, msg)
} }
} }

View File

@ -42,7 +42,6 @@ type memeText struct {
XPerc float64 `json:"x"` XPerc float64 `json:"x"`
YPerc float64 `json:"y"` YPerc float64 `json:"y"`
Text string `json:"t"` Text string `json:"t"`
Caps bool `json:"c"`
} }
var horizon = 24 * 7 var horizon = 24 * 7
@ -330,8 +329,8 @@ func (p *MemePlugin) findFontSize(config []memeText, w, h int, sizes []float64)
func defaultFormatConfig() []memeText { func defaultFormatConfig() []memeText {
return []memeText{ return []memeText{
{XPerc: 0.5, YPerc: 0.05, Caps: true}, {XPerc: 0.5, YPerc: 0.05},
{XPerc: 0.5, YPerc: 0.95, Caps: true}, {XPerc: 0.5, YPerc: 0.95},
} }
} }
@ -396,12 +395,6 @@ func (p *MemePlugin) genMeme(meme string, bully image.Image, config []memeText)
fontLocation := p.c.Get("meme.font", "impact.ttf") fontLocation := p.c.Get("meme.font", "impact.ttf")
m.LoadFontFace(fontLocation, p.findFontSize(config, w, h, fontSizes)) m.LoadFontFace(fontLocation, p.findFontSize(config, w, h, fontSizes))
for i, c := range config {
if c.Caps {
config[i].Text = strings.ToUpper(c.Text)
}
}
// Apply black stroke // Apply black stroke
m.SetHexColor("#000") m.SetHexColor("#000")
strokeSize := 6 strokeSize := 6

View File

@ -25,7 +25,7 @@ var memeIndex = `
<b-navbar> <b-navbar>
<b-navbar-brand>Memes</b-navbar-brand> <b-navbar-brand>Memes</b-navbar-brand>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-for="item in nav" :href="item.url" :active="item.name === 'Meme'">{{ item.name }}</b-nav-item> <b-nav-item v-for="item in nav" :href="item.URL" :active="item.Name === 'Meme'">{{ "{{ item.Name }}" }}</b-nav-item>
</b-navbar-nav> </b-navbar-nav>
</b-navbar> </b-navbar>
<b-alert <b-alert
@ -33,53 +33,9 @@ var memeIndex = `
variant="error" variant="error"
v-if="err" v-if="err"
@dismissed="err = ''"> @dismissed="err = ''">
{{ err }} {{ "{{ err }}" }}
</b-alert> </b-alert>
<b-form @submit="saveConfig" v-if="editConfig"> <b-form @submit="addMeme">
<b-container>
<b-row>
<b-col cols="1">
Name:
</b-col>
<b-col>
{{ editConfig.name }}
</b-col>
</b-row>
<b-row>
<b-col cols="1">
Image:
</b-col>
<b-col>
<img :src="editConfig.url" :alt="editConfig.url" rounded block fluid />
</b-col>
</b-row>
<b-row>
<b-col cols="1">
URL:
</b-col>
<b-col>
<b-input placeholder="URL..." v-model="editConfig.url"></b-input>
</b-col>
</b-row>
<b-row>
<b-col cols="1">
Config:
</b-col>
<b-col>
<b-form-textarea v-model="editConfig.config" rows="10">
</b-form-textarea>
</b-col>
</b-row>
<b-row>
<b-button type="submit" variant="primary">Save</b-button>
&nbsp;
<b-button @click="rm" variant="danger">Delete</b-button>
&nbsp;
<b-button type="cancel" @click="editConfig = null" variant="secondary">Cancel</b-button>
</b-row>
</b-container>
</b-form>
<b-form @submit="addMeme" v-if="!editConfig">
<b-container> <b-container>
<b-row> <b-row>
<b-col cols="3"> <b-col cols="3">
@ -102,8 +58,7 @@ var memeIndex = `
:items="results" :items="results"
:fields="fields"> :fields="fields">
<template v-slot:cell(config)="data"> <template v-slot:cell(config)="data">
<pre>{{data.item.config | pretty}}</pre> <pre>{{ "{{data.item.config}}" }}</pre>
<b-button @click="startEdit(data.item)">Edit</b-button>
</template> </template>
<template v-slot:cell(image)="data"> <template v-slot:cell(image)="data">
<b-img :src="data.item.url" rounded block fluid /> <b-img :src="data.item.url" rounded block fluid />
@ -125,33 +80,22 @@ var memeIndex = `
router, router,
data: { data: {
err: '', err: '',
nav: [], nav: {{ .Nav }},
name: '', name: "",
url: '', url: "",
config: '', config: "",
results: [], results: [],
editConfig: null,
fields: [ fields: [
{ key: 'name', sortable: true }, { key: 'name', sortable: true },
{ key: 'url', sortable: true },
{ key: 'config' }, { key: 'config' },
{ key: 'image' } { key: 'image' }
] ]
}, },
mounted() { mounted() {
axios.get('/nav')
.then(resp => {
this.nav = resp.data;
})
.catch(err => console.log(err))
this.refresh(); this.refresh();
}, },
filters: { computed: {
pretty: function(value) {
if (!value) {
return ""
}
return JSON.stringify(JSON.parse(value), null, 2);
}
}, },
methods: { methods: {
refresh: function() { refresh: function() {
@ -176,36 +120,6 @@ var memeIndex = `
this.refresh(); this.refresh();
}) })
.catch(err => (this.err = err)); .catch(err => (this.err = err));
},
startEdit: function(item) {
this.editConfig = item;
},
saveConfig: function(evt) {
if (evt) {
evt.preventDefault();
evt.stopPropagation();
}
if (this.editConfig && this.editConfig.name && this.editConfig.url) {
axios.post('/meme/add', this.editConfig)
.then(resp => {
this.results = resp.data;
this.editConfig = null;
this.refresh();
})
.catch(err => this.err = err);
}
},
rm: function(evt) {
if (evt) {
evt.preventDefault();
evt.stopPropagation();
}
axios.delete('/meme/rm', { data: this.editConfig })
.then(resp => {
this.editConfig = null;
this.refresh();
})
.catch(err => this.err = err);
} }
} }
}) })

View File

@ -3,6 +3,7 @@ package meme
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
@ -46,7 +47,7 @@ func (p *MemePlugin) all(w http.ResponseWriter, r *http.Request) {
for n, u := range memes { for n, u := range memes {
config, ok := configs[n] config, ok := configs[n]
if !ok { if !ok {
b, _ := json.Marshal(defaultFormatConfig()) b, _ := json.Marshal(defaultFormatConfig)
config = string(b) config = string(b)
} }
realURL, err := url.Parse(u) realURL, err := url.Parse(u)
@ -129,7 +130,8 @@ func (p *MemePlugin) addMeme(w http.ResponseWriter, r *http.Request) {
} }
func (p *MemePlugin) webRoot(w http.ResponseWriter, r *http.Request) { func (p *MemePlugin) webRoot(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, memeIndex) var tpl = template.Must(template.New("factoidIndex").Parse(string(memeIndex)))
tpl.Execute(w, struct{ Nav []bot.EndPoint }{p.bot.GetWebNavigation()})
} }
func (p *MemePlugin) img(w http.ResponseWriter, r *http.Request) { func (p *MemePlugin) img(w http.ResponseWriter, r *http.Request) {