matrix/rss.go
2024-11-21 20:09:48 +08:00

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![Image](%s)", 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
}
}