133 lines
3.8 KiB
Go
133 lines
3.8 KiB
Go
package send
|
|
|
|
import (
|
|
"brainbaking.com/go-jamming/app/mf"
|
|
"brainbaking.com/go-jamming/app/pingback/send"
|
|
"brainbaking.com/go-jamming/common"
|
|
"brainbaking.com/go-jamming/db"
|
|
"brainbaking.com/go-jamming/rest"
|
|
"fmt"
|
|
"github.com/rs/zerolog/log"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type Sender struct {
|
|
RestClient rest.Client
|
|
Conf *common.Config
|
|
Repo db.MentionRepo
|
|
}
|
|
|
|
// SendSingle sends out webmentions serially for a single source.
|
|
// It does validate the relative path against the domain, which is supposed to be served using https.
|
|
func (snder *Sender) SendSingle(domain string, relSource string) {
|
|
source := fmt.Sprintf("https://%s/%s", domain, relSource)
|
|
log.Info().Str("url", source).Msg(` OK: someone wants to send a single mention`)
|
|
|
|
_, html, err := snder.RestClient.GetBody(source)
|
|
if err != nil {
|
|
log.Err(err).Str("url", source).Msg("Unable to validate source, send aborted")
|
|
return
|
|
}
|
|
|
|
for _, href := range snder.collectUniqueHrefsFromHtml(html) {
|
|
if strings.HasPrefix(href, "http") {
|
|
snder.sendMention(mf.Mention{
|
|
Source: source,
|
|
Target: href,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send sends out multiple webmentions based on since and what's posted in the RSS feed.
|
|
// It first GETs domain/index.xml and goes from there.
|
|
func (snder *Sender) Send(domain string) {
|
|
lastSent := snder.Repo.LastSentMention(domain)
|
|
|
|
log.Info().Str("domain", domain).Str("lastsent", lastSent).Msg(` OK: someone wants to send mentions`)
|
|
feedUrl, err := snder.discoverRssFeed(domain)
|
|
if err != nil {
|
|
log.Err(err).Str("url", feedUrl).Msg("Unable to retrieve RSS feed, send aborted")
|
|
return
|
|
}
|
|
_, feed, err := snder.RestClient.GetBody(feedUrl)
|
|
// just to be sure. Should not produce an error due to check above, but you never know.
|
|
if err != nil {
|
|
log.Err(err).Str("url", feedUrl).Msg("Unable to retrieve RSS feed, send aborted")
|
|
return
|
|
}
|
|
|
|
lastSent, err = snder.parseRssFeed(feed, lastSent)
|
|
if err != nil {
|
|
log.Err(err).Str("url", feedUrl).Msg("Unable to parse RSS feed, send aborted")
|
|
return
|
|
}
|
|
|
|
snder.Repo.UpdateLastSentMention(domain, lastSent)
|
|
log.Info().Str("feed", feedUrl).Str("lastsent", lastSent).Msg(` OK: send processed.`)
|
|
}
|
|
|
|
func (snder *Sender) parseRssFeed(feed string, lastSentLink string) (string, error) {
|
|
items, err := snder.Collect(feed, lastSentLink)
|
|
if err != nil {
|
|
return lastSentLink, err
|
|
}
|
|
if len(items) == 0 {
|
|
return lastSentLink, nil
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
sema := make(chan struct{}, 20)
|
|
|
|
for _, item := range items {
|
|
for _, href := range item.hrefs {
|
|
if strings.HasPrefix(href, "http") {
|
|
mention := mf.Mention{
|
|
// SOURCE is own domain this time, TARGET = outbound
|
|
Source: item.link,
|
|
Target: href,
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func() {
|
|
sema <- struct{}{}
|
|
defer func() { <-sema }()
|
|
defer wg.Done()
|
|
snder.sendMention(mention)
|
|
}()
|
|
}
|
|
}
|
|
}
|
|
wg.Wait()
|
|
// first item is the most recent one!
|
|
return items[0].link, nil
|
|
}
|
|
|
|
var mentionFuncs = map[string]func(snder *Sender, mention mf.Mention, endpoint string){
|
|
typeUnknown: func(snder *Sender, mention mf.Mention, endpoint string) {},
|
|
typeWebmention: sendMentionAsWebmention,
|
|
typePingback: sendMentionAsPingback,
|
|
}
|
|
|
|
func (snder *Sender) sendMention(mention mf.Mention) {
|
|
endpoint, mentionType := snder.discoverMentionEndpoint(mention.Target)
|
|
mentionFuncs[mentionType](snder, mention, endpoint)
|
|
}
|
|
|
|
func sendMentionAsWebmention(snder *Sender, mention mf.Mention, endpoint string) {
|
|
err := snder.RestClient.PostForm(endpoint, mention.AsFormValues())
|
|
if err != nil {
|
|
log.Err(err).Str("endpoint", endpoint).Stringer("wm", mention).Msg("Webmention send failed")
|
|
return
|
|
}
|
|
log.Info().Str("endpoint", endpoint).Stringer("wm", mention).Msg("OK: webmention sent.")
|
|
}
|
|
|
|
func sendMentionAsPingback(snder *Sender, mention mf.Mention, endpoint string) {
|
|
pingbackSender := &send.Sender{
|
|
RestClient: snder.RestClient,
|
|
}
|
|
pingbackSender.SendPingbackToEndpoint(endpoint, mention)
|
|
}
|