284 lines
6.7 KiB
Go
284 lines
6.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-yaml/yaml"
|
|
"github.com/mattn/go-mastodon"
|
|
"github.com/mmcdole/gofeed"
|
|
"github.com/urfave/cli/v3"
|
|
"golang.org/x/net/html"
|
|
"golang.org/x/net/html/atom"
|
|
)
|
|
|
|
type MastodonClientData struct {
|
|
ClientID string `yaml:"clientID,omitempty"`
|
|
ClientSecret string `yaml:"clientSecret,omitempty"`
|
|
AccessToken string `yaml:"accessToken,omitempty"`
|
|
Instance string `yaml:"instance,omitempty"`
|
|
}
|
|
|
|
type KikiSettings struct {
|
|
Instance string `yaml:"instance,omitempty"`
|
|
RSSUri string `yaml:"rss_url,omitempty"`
|
|
}
|
|
|
|
func getDataFromConfig(path string) *mastodon.Config {
|
|
var clientData MastodonClientData
|
|
secretConfig, err := os.ReadFile(path)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
err = yaml.Unmarshal(secretConfig, &clientData)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
config := &mastodon.Config{
|
|
Server: clientData.Instance,
|
|
ClientID: clientData.ClientID,
|
|
ClientSecret: clientData.ClientSecret,
|
|
AccessToken: clientData.AccessToken,
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
func returnImgArray(htmlText string) []string {
|
|
var imgArray []string
|
|
parser, err := html.Parse(strings.NewReader(htmlText))
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
for n := range parser.Descendants() {
|
|
if n.Type == html.ElementNode && n.DataAtom == atom.Img {
|
|
for _, img := range n.Attr {
|
|
if img.Key == "src" {
|
|
imgArray = append(imgArray, img.Val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return imgArray
|
|
}
|
|
|
|
func hasHTMLTags(s string) bool {
|
|
re := regexp.MustCompile(`<(?i)[a-z][a-z0-9]*[^>]*>`)
|
|
return re.MatchString(s)
|
|
}
|
|
|
|
func getKikiConfig(path string) KikiSettings {
|
|
var kikiSettings KikiSettings
|
|
kikiConfigFile, err := os.ReadFile(path)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
err = yaml.Unmarshal(kikiConfigFile, &kikiSettings)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
return kikiSettings
|
|
}
|
|
|
|
func clientConfiguration(Instance string) {
|
|
appConfig := &mastodon.AppConfig{
|
|
Server: Instance,
|
|
ClientName: "Kiki",
|
|
Scopes: "read write follow",
|
|
Website: "catgirls.asia",
|
|
RedirectURIs: "urn:ietf:wg:oauth:2.0:oob",
|
|
}
|
|
app, err := mastodon.RegisterApp(context.Background(), appConfig)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
u, err := url.Parse(app.AuthURI)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
var userToken string
|
|
fmt.Println(u)
|
|
fmt.Scanln(&userToken)
|
|
|
|
config := &mastodon.Config{
|
|
Server: Instance,
|
|
ClientID: app.ClientID,
|
|
ClientSecret: app.ClientSecret,
|
|
AccessToken: userToken,
|
|
}
|
|
|
|
mastoClient := mastodon.NewClient(config)
|
|
err = mastoClient.AuthenticateToken(context.Background(), userToken, "urn:ietf:wg:oauth:2.0:oob")
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
clientData := MastodonClientData{
|
|
Instance: Instance,
|
|
ClientID: mastoClient.Config.ClientID,
|
|
ClientSecret: mastoClient.Config.ClientSecret,
|
|
AccessToken: mastoClient.Config.AccessToken,
|
|
}
|
|
|
|
marshaledYaml, err := yaml.Marshal(clientData)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
log.Println(string(marshaledYaml))
|
|
secretConfig, err := os.OpenFile("secret.conf", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
secretConfig.Write(marshaledYaml)
|
|
defer secretConfig.Close()
|
|
}
|
|
|
|
func newsText(url string) []*gofeed.Item {
|
|
fp := gofeed.NewParser()
|
|
feed, err := fp.ParseURL(url)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
log.Println("RSS лента получена")
|
|
return feed.Items
|
|
}
|
|
|
|
func createPost(statusText string) {
|
|
config := getDataFromConfig("secret.conf")
|
|
|
|
mastoClient := mastodon.NewClient(config)
|
|
|
|
toot := mastodon.Toot{
|
|
Status: statusText,
|
|
Visibility: "unlisted",
|
|
}
|
|
|
|
_, err := mastoClient.PostStatus(context.Background(), &toot)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
}
|
|
|
|
func picBytesArray(picturesArray []string) [][]byte {
|
|
var picturesBytes [][]byte
|
|
for _, picture := range picturesArray {
|
|
resp, err := http.Get(picture)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return picturesBytes
|
|
}
|
|
defer resp.Body.Close()
|
|
picBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return picturesBytes
|
|
}
|
|
picturesBytes = append(picturesBytes, picBytes)
|
|
}
|
|
return picturesBytes
|
|
}
|
|
|
|
func uploadPictures(filesBytes [][]byte) []*mastodon.Attachment {
|
|
config := getDataFromConfig("secret.conf")
|
|
var attachments []*mastodon.Attachment
|
|
mastoClient := mastodon.NewClient(config)
|
|
for _, file := range filesBytes {
|
|
att, err := mastoClient.UploadMediaFromBytes(context.Background(), file)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return attachments
|
|
}
|
|
attachments = append(attachments, att)
|
|
}
|
|
return attachments
|
|
}
|
|
|
|
func postWithPictures(attachments []*mastodon.Attachment) {
|
|
var mediaID []mastodon.ID
|
|
config := getDataFromConfig("secret.conf")
|
|
mastoClient := mastodon.NewClient(config)
|
|
for _, attach := range attachments {
|
|
mediaID = append(mediaID, attach.ID)
|
|
}
|
|
toot := mastodon.Toot{
|
|
MediaIDs: mediaID,
|
|
Visibility: "unlisted",
|
|
Sensitive: true,
|
|
}
|
|
_, err := mastoClient.PostStatus(context.Background(), &toot)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
}
|
|
|
|
func main() {
|
|
cmd := &cli.Command{
|
|
Name: "kiki",
|
|
Usage: "Ретранслятор из RSS в Mastodon. Когда-нибудь...",
|
|
Commands: []*cli.Command{
|
|
{
|
|
Name: "init",
|
|
Usage: "Инициализировать клиента",
|
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
|
confFile, err := filepath.Abs("config.yaml")
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
kikiConfig := getKikiConfig(confFile)
|
|
instanceUrlParser, err := url.Parse(kikiConfig.Instance)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
instanceUrlParser.Scheme = "https"
|
|
clientConfiguration(instanceUrlParser.String())
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "run",
|
|
Usage: "Запуск транслятора",
|
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
|
var lastGUID string
|
|
confFile, err := filepath.Abs("config.yaml")
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
kikiConfig := getKikiConfig(confFile)
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
defer ticker.Stop()
|
|
for range ticker.C {
|
|
news := newsText(kikiConfig.RSSUri)
|
|
if news[0].GUID != lastGUID {
|
|
if !hasHTMLTags(news[0].Description) {
|
|
log.Println(news[0].Description)
|
|
createPost(news[0].Description)
|
|
lastGUID = news[0].GUID
|
|
log.Println("Пост отправлен")
|
|
} else {
|
|
attachments := uploadPictures(picBytesArray(returnImgArray(news[0].Description)))
|
|
postWithPictures(attachments)
|
|
lastGUID = news[0].GUID
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := cmd.Run(context.Background(), os.Args); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|