174 lines
4.1 KiB
Go
174 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"github.com/matrix-org/gomatrix"
|
|
"github.com/mmcdole/gofeed"
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
type Config struct {
|
|
Homeserver string `yaml:"homeserver"`
|
|
Username string `yaml:"username"`
|
|
Password string `yaml:"password"`
|
|
Feeds []struct {
|
|
URL string `yaml:"url"`
|
|
Alias string `yaml:"alias"`
|
|
} `yaml:"feeds"`
|
|
}
|
|
|
|
var (
|
|
client *gomatrix.Client
|
|
processedFeed map[string]map[string]bool // 保存已处理的条目链接
|
|
)
|
|
|
|
func loadConfig(filename string) (*Config, error) {
|
|
data, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var config Config
|
|
err = yaml.Unmarshal(data, &config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &config, nil
|
|
}
|
|
|
|
func login(config *Config) error {
|
|
var err error
|
|
client, err = gomatrix.NewClient(config.Homeserver, "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp, err := client.Login(&gomatrix.ReqLogin{
|
|
Type: "m.login.password",
|
|
User: config.Username,
|
|
Password: config.Password,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client.SetCredentials(resp.UserID, resp.AccessToken)
|
|
return nil
|
|
}
|
|
|
|
func processFeed(fp *gofeed.Parser, feedURL, alias string) error {
|
|
rssFeed, err := fp.ParseURL(feedURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse feed %s: %v", feedURL, err)
|
|
}
|
|
|
|
if processedFeed[alias] == nil {
|
|
processedFeed[alias] = make(map[string]bool)
|
|
}
|
|
|
|
for _, item := range rssFeed.Items {
|
|
if processedFeed[alias][item.Link] {
|
|
continue // Skip already processed items
|
|
}
|
|
|
|
// Extract and format message content
|
|
message := formatMessage(item)
|
|
htmlMessage := formatHTMLMessage(item)
|
|
|
|
_, err := client.SendMessageEvent(alias, "m.room.message", map[string]interface{}{
|
|
"msgtype": "m.text",
|
|
"body": message,
|
|
"format": "org.matrix.custom.html",
|
|
"formatted_body": htmlMessage,
|
|
})
|
|
if err != nil {
|
|
log.Printf("Failed to send message to %s: %v", alias, err)
|
|
} else {
|
|
log.Printf("Successfully sent message to %s", alias)
|
|
processedFeed[alias][item.Link] = true
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func formatMessage(item *gofeed.Item) string {
|
|
message := fmt.Sprintf("**%s**\n%s\n\n%s", item.Title, item.Published, item.Link)
|
|
if item.Description != "" {
|
|
message += fmt.Sprintf("\n\n%s", extractText(item.Description))
|
|
}
|
|
if item.Image != nil {
|
|
message += fmt.Sprintf("\n\n", item.Image.URL)
|
|
}
|
|
if item.Author != nil {
|
|
message += fmt.Sprintf("\n\nAuthor: %s", item.Author.Name)
|
|
}
|
|
message += "\n\nSponsor: 更流畅的富强节点,与时代共「加速」"
|
|
return message
|
|
}
|
|
|
|
func formatHTMLMessage(item *gofeed.Item) string {
|
|
htmlMessage := fmt.Sprintf("<strong>%s</strong><br>%s<br><a href=\"%s\">%s</a><br><br>%s",
|
|
item.Title, item.Published, item.Link, item.Link, extractText(item.Description))
|
|
if item.Image != nil {
|
|
htmlMessage += fmt.Sprintf("<br><img src=\"%s\" alt=\"Image\">", item.Image.URL)
|
|
}
|
|
if item.Author != nil {
|
|
htmlMessage += fmt.Sprintf("<br><br>Author: %s", item.Author.Name)
|
|
}
|
|
htmlMessage += "<br><br>Sponsor: 更流畅的富强节点,与时代共「加速」"
|
|
return htmlMessage
|
|
}
|
|
|
|
// Extract text content from HTML
|
|
func extractText(htmlContent string) string {
|
|
doc, err := html.Parse(strings.NewReader(htmlContent))
|
|
if err != nil {
|
|
log.Printf("Failed to parse HTML content: %v", err)
|
|
return ""
|
|
}
|
|
var textContent strings.Builder
|
|
var f func(*html.Node)
|
|
f = func(n *html.Node) {
|
|
if n.Type == html.TextNode {
|
|
textContent.WriteString(n.Data)
|
|
}
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
f(c)
|
|
}
|
|
}
|
|
f(doc)
|
|
return textContent.String()
|
|
}
|
|
|
|
func main() {
|
|
config, err := loadConfig("config.yaml")
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
err = login(config)
|
|
if err != nil {
|
|
log.Fatalf("Failed to login: %v", err)
|
|
}
|
|
|
|
fp := gofeed.NewParser()
|
|
processedFeed = make(map[string]map[string]bool)
|
|
|
|
ticker := time.NewTicker(10 * time.Minute)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
for _, feed := range config.Feeds {
|
|
err := processFeed(fp, feed.URL, feed.Alias)
|
|
if err != nil {
|
|
log.Printf("Error processing feed %s: %v", feed.URL, err)
|
|
}
|
|
}
|
|
<-ticker.C
|
|
}
|
|
}
|